我们知道 Vue 模板是非常强大的,基本可以完成我们日常开发的所有任务。但是,有一些用例,如基于输入或插槽值创建动态组件方式,render
函数会比模板完成的更好也更出色。
用过 React 开发的人对 render
函数应该非常熟悉,因为React组件通过 JSX和 render 函数来构建的。尽管Vue render
函数也可以用JSX编写,但在这里我们使用原生 JS方式,因为这样,我们可以更轻松地了解Vue组件系统的一些基础。
值得注意的是,Vue 的模板实际上在编译时也是会先解析成 render
函数表示方式。模板只是在render
函数之上提供了一个方便且熟悉的语法糖。尽管 render
函数更强大,但render
函数可读性很差,相对用的也比较少了。
创建组件
带有 render
函数的组件没有template
标记或属性。相反,该组件定义了一个了名为render
的函数,该函数接收一个reateElement(renderElement: String | Component, definition: Object, children: String | Array)
参数(由于某种原因,通常别名为h
,归咎于JSX)并返回使用该函数创建的元素,其他一切保持不变,来看看事例:
export default {
data() {
return {
isRed: true
}
},
/*
* <template>
* <div :class='{'is-red': isRed}'>
* <p>这是一个 render 事例</p>
* </div>
* </template>
*/
// render 中的渲染结果与上面等价
render(h) {
return h('div', {
'class': {
'is-red': this.isRed
}
}, [
h('p', '这是一个 render 事例')
])
}
}
render 函数中如何表示指令
Vue 模板具有各种便捷功能,以便向模板添加基本逻辑和绑定功能,如 v-if
、v-for
、v-moel
指令等。在render
函数中是无法使用这些指令的。取而代之的是以纯 JS 来实现,对于大多数指令而言,这也是比较简单的。
v-if
v-if
用纯 JS 实现很简单,只需围绕createElement
调用使用 if(expr)
语句即可。
v-for
v-for
可以使用for-of
,Array.map
,Array.filter
等的JS方法中的任何一种来实现。我们可以通过非常有趣的方式将它们组合在一起,以实现过滤或状态切片,而无需计算属性。
例如,有以下 Vue 的模板代码
<template>
<ul>
<li v-for='pea of pod'>
</li>
</ul>
</template>
可以用下面的 render
函数来实现上面的效果:
render(h) {
return h('ul', this.pod.map(pea => h('li', pea.name)));
}
v-model
我们知道,v-model
只是bind
属性与value
的语法糖,并在触发input
事件时设置数据属性。但是,在render
函数没有这样的简写,我们需要自己实现。
假设,在 Vue 中,我们有如下的结构:
<template>
<input v-model='myBoundProperty'/>
</template>
上面代码等价于:
<template>
<input :value='myBoundProperty' @input='myBoundProperty = $event.target.value'/>
</template>
在 render 函数中可以用下面方式来实现上面的代码:
render(h) {
return h('input', {
domProps: {
value: this.myBoundProperty
},
on: {
input: e => {
this.myBoundProperty = e.target.value
}
}
})
}
v-bind
attribute
和property
这两种类型的绑定被放在元素定义中,如arttrs
、props
和domProps
( value
和innerHTML
之类的东西)。
render(h) {
return h('div', {
attrs: {
// <div :id='myCustomId'>
id: this.myCustomId
},
props: {
// <div :someProp='someonePutSomethingHere'>
someProp: this.someonePutSomethingHere
},
domProps: {
// <div :value='somethingElse'>
value: this.somethingElse
}
});
}
需要注意的是,对于 class
和style
的绑定是直接在定义的根进行处理,而不是作为attrs
,props
或domProps
处理。
render(h) {
return h('div', {
// “类”是JS中的保留关键字,因此必须引用它。
'class': {
myClass: true,
theirClass: false
},
style: {
backgroundColor: 'green'
}
});
}
v-on
对事件处理程也是直接添加到元素定义中 on
定义
render(h) {
return h('div', {
on: {
click(e) {
console.log('I got clickeded!')
}
}
});
}
事件的修饰符可以在处理程序内部实现:
.stop -> e.stopPropagation()
.prevent -> e.preventDefault()
.self -> if (e.target !== e.currentTarget) return
键盘修饰符
.[TARGET_KEY_CODE]
-> if (event.keyCode !== TARGET_KEY_CODE) return
.[MODIFIER]
-> if (!event.MODIFIERKey) return
特殊属性
Slots
可以通过this.$slots
作为createElement()
节点的数组来访问插槽。
作用域插槽存储在this.$scopedSlots[scope](props:object)
中,作为返回createElement()
节点数组的函数。
原文:https:///introduction-to-vue-render-functions-with-examples