通常情况下,在我们使用模板的时候,不太方便在模板内部根据需要切换不同的标签,如:提供一个参数,根据这个参数模板返回h1-h7的标签,并填上内容,一般情况下会是这样——
var component1 = {
data:function(){
return {
hdata:"你好啊",
level:2
}
},
template: `
<div>
<h1 v-if='level==1'>{{hdata}}</h1>
<h2 v-if='level==2'>{{hdata}}</h2>
<h3 v-if='level==3'>{{hdata}}</h3>
<h4 v-if='level==4'>{{hdata}}</h4>
</div>
`
};
但这样会使得内容过于繁重,所以出现了渲染函数。
渲染函数可以将如上的模板代码,用js代码写出来,使得内容变得简练:
var component2 = {
data: function () {
return {
hdata: "你好啊2",
level: 2
}
},
render: function (createEle) {
var arr = [createEle("div", "ccc"), createEle("div", "ccc")]
return createEle(
"h" + this.level, { class: "aaa" },
arr
)
}
}
如上,使用render函数代替了template,该参数接受一个createElement方法参数,调用这个方法会生成html代码。
这个方法接受三个参数,第一个是标签名,第二个是这个标签的属性,第三个是标签内容。
createEle可以理解为创建节点的方法,可以创造一个标签,赋予标签的属性,并且填入内容。
其内容是可以其他createEle创造出来的元素。
如上例。
节点,树和虚拟dom
标签,标签的内容,标签的属性都可以算作节点,而这些节点组合起来的就是树,而vue使用的节点并非实际的dom,而是虚拟的vue节点。
虚拟dom创造方法:createElement
这个方法会告诉vue页面需要渲染什么样的节点。
该方法的三个参数分别需要:
// {String | Object | Function}
// 一个 HTML 标签名、组件选项对象,或者
// resolve 了上述任何一种的一个 async 函数。必填项。
'div',
// {Object}
// 一个与模板中属性对应的数据对象。可选。
{
// (详情见下一节)
},
/ {String | Array}
// 子级虚拟节点 (VNodes),由 `createElement()` 构建而成,
// 也可以使用字符串来生成“文本虚拟节点”。可选。
上下两个参数理解起来较为简单,中间的参数比较复杂:
{
// 与 `v-bind:class` 的 API 相同,
// 接受一个字符串、对象或字符串和对象组成的数组
'class': {
foo: true,
bar: false
},
// 与 `v-bind:style` 的 API 相同,
// 接受一个字符串、对象,或对象组成的数组
style: {
color: 'red',
fontSize: '14px'
},
// 普通的 HTML 特性
attrs: {
id: 'foo'
},
// 组件 prop
props: {
myProp: 'bar'
},
// DOM 属性
domProps: {
innerHTML: 'baz'
},
// 事件监听器在 `on` 属性内,
// 但不再支持如 `v-on:keyup.enter` 这样的修饰器。
// 需要在处理函数中手动检查 keyCode。
on: {
click: this.clickHandler
},
// 仅用于组件,用于监听原生事件,而不是组件内部使用
// `vm.$emit` 触发的事件。
nativeOn: {
click: this.nativeClickHandler
},
// 自定义指令。注意,你无法对 `binding` 中的 `oldValue`
// 赋值,因为 Vue 已经自动为你进行了同步。
directives: [
{
name: 'my-custom-directive',
value: '2',
expression: '1 + 1',
arg: 'foo',
modifiers: {
bar: true
}
}
],
// 作用域插槽的格式为
// { name: props => VNode | Array<VNode> }
scopedSlots: {
default: props => createElement('span', props.text)
},
// 如果组件是其它组件的子组件,需为插槽指定名称
slot: 'name-of-slot',
// 其它特殊顶层属性
key: 'myKey',
ref: 'myRef',
// 如果你在渲染函数中给多个元素都应用了相同的 ref 名,
// 那么 `$refs.myRef` 会变成一个数组。
refInFor: true
}
``
理解了以上参数之后,渲染便可以这样书写:
var getChildrenTextContent = function (children) {
return children.map(function (node) {
return node.children
? getChildrenTextContent(node.children)
: node.text
}).join('')
}
Vue.component('anchored-heading', {
render: function (createElement) {
// 创建 kebab-case 风格的 ID
var headingId = getChildrenTextContent(this.)/g, '')
return createElement(
'h' + this.level,
[
createElement('a', {
attrs: {
name: headingId,
href: '#' + headingId
}
}, this.$slots.default)
]
)
},
props: {
level: {
type: Number,
required: true
}
}
})
### 渲染函数的利弊
渲染函数不能使用相当的组件,如v-model,对应的逻辑必须自己手动去实现:
v-if,v-for
props: ['items'],
render: function (createElement) {
if (this.items.length) {
return createElement('ul', this.items.map(function (item) {
return createElement('li', item.name)
}))
} else {
return createElement('p', 'No items found.')
}
}
v-model
props: ['value'],
render: function (createElement) {
var self = this
return createElement('input', {
domProps: {
value: self.value
},
on: {
input: function (event) {
self.$emit('input', event.target.value)
}
}
})
}
### 插槽
你可以通过`this.$slots`来访问静态插槽的内容,每个插槽都是一个vnode数组。
可以通过`this.$scopedSlots`访问作用域插槽。
### JSX
网友评论