手记

Vue2.0文档学习及案例总结之----Render Functions

Vue2.0文档学习及案例总结之----Render Functions

以下学习总结,目前对于JSX和functional render尚未全面分析,但大多数常规做法都有了分析,这个将在后继文档中补充。


<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Render</title>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
</head>
<body>
<div id="demo"></div>
<script>
//    ES6模板字符串
var basket = {count: '8000', onSale: 'Yes'};
document.querySelector('body').append(`
There are <b>${basket.count}</b> items
in your basket, <em>${basket.onSale}</em>
are on sale!`
);

</script>
<h4>That template doesn’t feel great.</h4>
<p>内容分发一般适用于根标签相同内容不同的DOM结构</p>
<div id="app">
<anchored-heading :level="1">Hello world!</anchored-heading>
<anchored-heading :level="2">Hello world!</anchored-heading>
<anchored-heading :level="3">Hello world!</anchored-heading>
<anchored-heading :level="4">Hello world!</anchored-heading>
<anchored-heading :level="5">Hello world!</anchored-heading>
<anchored-heading :level="6">Hello world!</anchored-heading>
</div>
<script type="text/x-template" id="anchored-heading-template" name="anchoredHeading">
<div>
<h1 v-if="level === 1">
<slot></slot>
</h1>
<p v-if="level === 2">
<slot></slot>
</p>
<h3 v-if="level === 3">
<slot></slot>
</h3>
<h4 v-if="level === 4">
<slot></slot>
</h4>
<h5 v-if="level === 5">
<slot></slot>
</h5>
<h6 v-if="level === 6">
<slot></slot>
</h6>
</div>
</script>
<script>
var aa = Vue.component('anchored-heading', {
template: '#anchored-heading-template',
props: {
level: {
type: Number,
required: true
}
}
});
new Vue({
el: '#app',
components: {
anchoredHeading: aa
}
})
</script>
<h4>So let’s try rewriting it with a render function:</h4>
<h4>理解slot</h4>
<div id="slot">
<blog-post>
<h1 slot="header">
About Me
</h1>
<p>Here's some page content, which will be included
in vm.$slots.default, because it's not inside a named
slot.</p>
<p slot="footer">
Copyright 2016 Evan You
</p>
<p>If I have some content down here, it will also be included in vm.$slots.default.</p>.
</blog-post>
</div>
<hr>
<script>
Vue.component('blog-post', {
render: function (createElement) {
var header = this.$slots.header;
var body = this.$slots.default;
var footer = this.$slots.footer;
return createElement('div', [
createElement('header', header),
createElement('div', body),
createElement('footer', footer)
])
}
});
let slot = new Vue({
el: '#slot'
})
</script>
<div id="app1">
<anchored-heading-a :level="3">Hello world!</anchored-heading-a>
<anchored-heading-a :level="1">Hello world!</anchored-heading-a>
</div>
<h5>createElement参数详细说明</h5>
<pre>
<code>
// @returns {VNode}
createElement(
// {String | Object | Function}
'div',
// {Object},属性对象,可以在 template 中使用.可选项.
{
'class': {
foo: true,
bar: false
},
style: {
color: 'red',
fontSize: '14px'
},
attrs: {
id: 'foo'
},
props: {
myProp: 'bar'
},
domProps: {
innerHTML: 'baz'
},
// 事件监听器基于 "on"
// 所以不再支持如 v-on:keyup.enter 修饰器
// 需要手动匹配 keyCode。
on: {
click: this.clickHandler
},
// 仅对于组件,用于监听原生事件,而不是组件使用 vm.$emit 触发的事件。
nativeOn: {
click: this.nativeClickHandler
},
// 自定义指令. 注意事项:不能对绑定的旧值设值
directives: [
{
name: 'my-custom-directive',
value: '2',
expression: '1 + 1',
arg: 'foo',
modifiers: {
bar: true
}
}
],
// Scoped slots in the form of
<!--// { name: props => VNode | Array<VNode> }-->
scopedSlots: {
default: props => h('span', props.text)
},
// 如果子组件有定义 slot 的名称
slot: 'name-of-slot',
// 其他特殊顶层属性
key: 'myKey',
ref: 'myRef'
},
// {String | Array},子节点(VNodes). 可选项.
[
createElement('h1', 'hello world'),
createElement(MyComponent, {
props: {
someProp: 'foo'
}
}),
'bar'
]
)
</code>
</pre>
<h5>局限</h5>
<pre>
<code>
render: function (createElement) {
var myParagraphVNode = createElement('p', 'hi')
return createElement('div', [
// Yikes - duplicate VNodes!
myParagraphVNode, myParagraphVNode
])
}
//如果你真的需要重复很多次的元素/组件,你可以使用工厂函数来实现
render: function (createElement) {
return createElement('div',
Array.apply(null, { length: 20 }).map(function () {
return createElement('p', 'hi')
})
)
}
//所有组件树中的 VNodes 必须唯一。这意味着,下面的 render function 是无效的:
</code>
</pre>
<script>
var ab = Vue.component('anchored-heading-a', {
render: function (createElement) {
return createElement(
// {String | Object | Function}
// 一个 HTML 标签,组件设置,或一个函数
// 必须 Return 上述其中一个
'h' + this.level, // tag name
this.$slots.default // array of children
)
},
props: {
level: {
type: Number,
required: true
}
}
});
new Vue({
el: '#app1',
components: {
anchoredHeadingA: ab
}
})
</script>
<h4>The Data Object In-Depth</h4>
<h4>Complete Example</h4>
<div id="see">
<anchored-heading-x :level="1"><p><span><b>Hello world x!</b></span></p></anchored-heading-x>
</div>
<script>
var getChildrenTextContent = function (children) {
return children.map(function (node) {
return node.children
? getChildrenTextContent(node.children)
: node.text
}).join('')
};
Vue.component('anchored-heading-x', {
render: function (createElement) {
var headingId = getChildrenTextContent(this.$slots.default)
.toLowerCase()
.replace(/\W+/g, '-')
.replace(/(^-|-$)/g, '');
console.log(this.$slots.default);
// alert(headingId);
// var myParagraphVNode = createElement('p', 'hi');
return createElement(
'h' + this.level,
[
createElement('a', {
attrs: {
id: 'test',
name: headingId,
href: '#' + headingId
},
'class': {
foo: true,
},
style: {
backgroundColor: 'red',
}
}, this.$slots.default)
// myParagraphVNode
// Render.html?_ijt=4pvp86032pd8l53fv14frqcjnk:230 Uncaught SyntaxError: Unexpected identifier
]
)
},
props: {
level: {
type: Number,
required: true
}
}
});
new Vue({
el: '#see'
})
</script>
<div id="app2">
<anchored-heading-b></anchored-heading-b>
</div>
<script>
var ac = Vue.component('anchored-heading-b', {
render: function (createElement) {
var that = this;
return createElement('ol',
Array.apply(null, {length: 3}).map(function () {
return createElement('li', that.msg)
})
)
},
data: function () {
return {
msg: 'hello Array.apply(null,{}.map()'
}
}
});
new Vue({
el: '#app2',
// props: ['msg'],
components: {
anchoredHeadingB: ac
}
})
</script>
<h4>Replacing Template Features with Plain JavaScript</h4>
<div id="app3">
<i>render rendering</i>
<anchored-heading-c>
</anchored-heading-c>
<i>list rendering</i>
<ul v-if="items.length">
<li v-for="item in items">{{ item.name }}</li>
</ul>
<p v-else>No items found.</p>
</div>
<script>
var ad = Vue.component('anchored-heading-c', {
render: function (createElement) {
if (this.items.length) {
return createElement('ul', this.items.map(function (item) {
return createElement('li', item.name)
}))
} else {
return createElement('p', 'v-else:No items found.')
}
},
data: function () {
return {
// items:[],
items: [{name: 'v-if:hello'}, {name: 'hello'}, {name: 'hello'}, {name: 'hello'}, {name: 'hello'}]
}
}
});
new Vue({
el: '#app3',
data: function () {
return {
//模板用
items: [{name: 'world'}, {name: 'world'}, {name: 'world'}, {name: 'hello'}, {name: 'hello'}]
}
},
components: {
ad
}
})
</script>
<b>fh探索思路。。。</b>
<div id="i">
<my-input></my-input>
</div>
<script>
Vue.component('my-input', {
render: function (createElement) {
if (this.items[0].name === 'world') {
return createElement('ol', this.items.map(function (item) {
return createElement('li', item.name)
}))
} else {
return createElement('p', 'No items found.')
}
},

    data: function () {
        return {
            items: [
                {name: 'world'},
                {name: 'hello'}
            ],
        }
    }
});
new Vue({
    el: '#i',
    data: {
        items: [

// {type: 'button',},
// {type: 'checkbox'},
// {type: 'color',name:'color'},
// {type: 'date'},
// {type: 'datetime'},
// {type: 'datetime-local'},
// {type: 'email '},
// {type: 'file '},
// {type: 'hidden'},
// {type: 'image'},
// {type: 'month'},
// {type: 'number '},
// {type: 'password '},
// {type: 'radio'},
// {type: 'range'},
// {type: 'reset'},
// {type: 'search'},
// {type: 'submit'},
// {type: 'tel'},
// {type: 'text '},
// {type: 'time '},
// {type: 'url '},
{type: 'week '},
]
}
})
</script>
<b>v-model需要自己完善逻辑</b>
<div id="inputs">
<inputs></inputs>
</div>
<script>
Vue.component('inputs', {
render: function (createElement) {
//保证this指向组件自身
var self = this;
return createElement('input', {
domProps: {
value: self.value
},
on: {
input: function (e) {
self.value = e.target.value
}
},
})
},
data: function () {
return {
value: 'hello'
}
}
});
let inputs = new Vue({
el: 'inputs',
})
</script>
<b>Slots</b>
<pre>
<code>
//You can access static slot contents as Arrays of VNodes from this.$slots:
render: function (createElement) {
// <div><slot></slot></div>
return createElement('div', this.$slots.default)
}
//And access scoped slots as functions that return VNodes from this.$scopedSlots:
render: function (createElement) {
<!--// <div><slot :text="msg"></slot></div>-->
return createElement('div', [
this.$scopedSlots.default({
text: this.msg
})
])
}
//To pass scoped slots to a child component using render functions, use the scopedSlots field in VNode data:
render (createElement) {
return createElement('div', [
createElement('child', {
// pass scopedSlots in the data object
<!--// in the form of { name: props => VNode | Array<VNode> }-->
scopedSlots: {
default: function (props) {
return h('span', props.text)
}
}
})
])
}
</code>
</pre>
<h4>JSX</h4>
<mark> 将 h 作为 createElement 的别名是一个通用惯例,你会发现在 Vue 生态系统中,实际上必须用到 JSX,如果在作用域中 h 失去作用, 在应用中会触发报错。
</mark>
<pre>
<code>
import AnchoredHeading from './AnchoredHeading.vue'
new Vue({
el: '#demo',
render (h) {
return (
<AnchoredHeading level={1}>
<span>Hello</span> world!
</AnchoredHeading>
)
}
})
</code>
</pre>
<div id="jsx">
<jsx></jsx>
</div>
<script>
//要用到babel
</script>
<h4>函数化组件</h4>
<mark>
一个 函数化组件 就像这样:标记组件为 functional, 这意味它是无状态(没有 data),无实例(没有 this 上下文)。
</mark>
<pre><code>
Vue.component('my-component', {
functional: true,
// 为了弥补缺少的实例, 提供第二个参数作为上下文
render: function (createElement, context) {
// ...
},
// Props 可选
props: {
// ...
}
//组件需要的一切都是通过上下文传递:
props: 提供props 的对象
children: VNode 子节点的数组
slots: slots 对象
data: 传递给组件的 data 对象
parent: 对父组件的引用
})
</code></pre>
<hr>
<div id="fl">
<smart-list></smart-list>
</div>
<script>
Vue.component('smart-list', {
//以下两步的改写,context便可访问外部数据,
functional: true,
render: function (createElement, context) {
return createElement('ul',this.msg+': hello');

    },
    props: {//提供props对象
        items: {
            type: Array,
            required: true
        },
        isOrdered: Boolean
    },
    data(){return {
        msg:'hello'
    }},
    children: [
        //VNode子节点数组
    ],
    parent: {
        //对父组件的引用
    }
});
new Vue({
    el: '#fl',
    data: {
        items: ['1', '2', '3']
    }
})

</script>
<p>虚拟DOM</p>
<div id="vd"></div>
<script>
const vda = new Vue({
el: '#vd',
data: {
see: 'red',
msg: 'hello world'
},
render(h) {
return h('div', {style: {background: this.see}}, [
h('span', {}, [this.msg])
]
);
}
});
setTimeout(function () {
vda.see = 'green';
vda.msg = 'to see visual DOM '
}, 300)
</script>
<hr>
<div class="fh">
<see msg="hello"></see>
</div>
<script>
const see = Vue.component('see', {
render: h=>h('div', {
'class': {
foo: true,
bar: false
},
style: {
color: 'red',
fontSize: '14px'
},
attrs: {
id: 'foo'
},
props: [
'msg'
],
domProps: {
innerHTML: 'baz',
// value: self.value
},
on: {
click: this.test,
// input: function (e) {
// self.value = e.target.value
// }
},
nativeOn: {
click: this.nativeClickHandler
},
directives: [
{
name: 'my-custom-directive',
value: '2',
expression: '1 + 1',
arg: 'foo',
modifiers: {
bar: true
}
}
],
scopedSlots: {
// default: props => h('span', props.text)
},
slot: 'name-of-slot',
key: 'myKey',
ref: 'myRef'
}, this.$slots.default),
methods: {
test: function () {
alert('ok')
}
}
});
const fh = new Vue({
el: '.fh',
components: {}
})
</script>
</body>
</html>

1人推荐
随时随地看视频
慕课网APP