前言
使用vue
这么久了,但是对render
函数还是不怎么熟悉,所以写篇文章加深理解和记忆!方便日后熟练使用。
正文
mian.js中的new Vue中会传入render: h => h(App)
那么这个h
其实可以用来创建元素。
他的第一个参数是必选参数
第二个和第三个都是可选参数
第一个参数就是当前要渲染的组件或者标签,也可以是个函数
第二个参数一个配置对象,里面可以传递一些html
自带的原生属性
第三个参数可以是字符串或者数组 示例代码:
new Vue({
render: h => {
return h('div',{
attrs:{
id:'box'
},
style:{
color:'red'
}
},'Dreams')
}
}).$mount('#app')
复制代码
这里我们用render
函数生成了一个id
为box
,颜色为红色
,文本为Dreams
的div
元素。
问题又来了,假设我们渲染一个组件,组件里面要进行传值,那么该怎么书写?
new Vue({
render: h => {
return h(CountTo,{
props:{
endVal:100
}
})
}
}).$mount('#app')
复制代码
那么这里我们就可以在render
函数里使用props
对组件进行传值操作了。
上面这个是一个数字过渡组件,我们传递endVal是为了告诉他到多少时停止
在此基础上,我们如果想给组件绑定事件呢?
new Vue({
render: h => {
return h(CountTo,{
props:{
endVal:100
},
on:{
'on-animation-end':(val) => {
console.log(val)
}
}
})
}
}).$mount('#app')
复制代码
实际上这里监听了一个事件,on-animation-end
这个事件其实是CountTo
组件通过$emit
派发出来的. 然后我们又想给这个组件最外层绑定一个点击事件该怎么做?
new Vue({
render: h => {
return h(CountTo,{
props:{
endVal:100
},
on:{
'on-animation-end':(val) => {
console.log(val)
}
},
nativeOn:{
'click':() => {
console.log('click')
}
}
})
}
}).$mount('#app')
复制代码
然后我们又想给这个组件定义一个class
类名怎么办?
new Vue({
render: h => {
return h(CountTo,{
'class':['count-to',true ? 'aa' : 'bb'],
props:{
endVal:100
},
on:{
'on-animation-end':(val) => {
console.log(val)
}
},
nativeOn:{
'click':() => {
console.log('click')
}
}
})
}
}).$mount('#app')
复制代码
那么我们还想使用指令和插槽并且获取dom
怎么办?
new Vue({
render: h => {
return h(CountTo,{
'class':['count-to',true ? 'aa' : 'bb'],
props:{
endVal:100
},
domProps:{
//dom上的一些属性,
//因为现在是组件所以这样写会报错
innerHTML:'123'
},
on:{
'on-animation-end':(val) => {
console.log(val)
}
},
nativeOn:{
'click':() => {
console.log('click')
}
},
directives:[],
slot:'default',
key:'',
ref:'',
scopedSlots:{},
})
}
}).$mount('#app')
复制代码
那假设现在需要给一个标签添加元素怎么添加?
我们把上面的干掉!
new Vue({
render: h => h('div','123')
}).$mount('#app')
复制代码
这里的123是第三个参数,第二个参数我们不写直接留空
所以页面上会渲染出
<div>123</div>
复制代码
如果我们要在div
元素里添加多个子元素呢? 那么肯定是个数组。
new Vue({
render: h => h('div',{},[
h('span','111'),
h('span','222'),
])
}).$mount('#app')
复制代码
那么现在div
里就有两个span
标签了
<div>
<span>111</span>
<span>222</span>
</div>
复制代码
那么我们生成的东西如果很多且不固定呢?
那么这里如何使用循环呢?
那么这里我们新建一个组件文件
里面的内容是这样的
new Vue({
render: h => h('div',{},[
h('ul',{
on:{
'click':(event) => {
console.log(event)
}
}
},[
h('li',{
on:{
'click':event => {
console.log(event)
}
}
})
])
])
}).$mount('#app')
复制代码
那么现在div
里就有一个ul
标签包含一个li
标签了
<div>
<ul>
<li></li>
</ul>
</div>
复制代码
在render
里我们其实是没办法使用v-for
指令的,所以我们要自己实现这个方法
let list = [{name:'Dreams'},{name:'Dreams2'}]
const getLiEleArr = h => {
return list.map(item => h('li',{
on:{
'click':(event) => {
console.log(event)
}
}
},item.name))//标签内的内容
}
复制代码
这个方法写好了之后我们把刚刚上面的改造一下
new Vue({
render: h => h('div',{},[
h('ul',{
on:{
'click':(event) => {
console.log(event)
}
}
},getLiEleArr(h))
])
}).$mount('#app')
复制代码
那么这样页面上就成功的渲染出如下的标签:
<div>
<ul>
<li>Dreams</li>
<li>Dreams2</li>
</ul>
</div>
复制代码
同时li和ul也有点击事件
这里需要阻止事件冒泡,但render
函数里也无法使用vue
的.stop
修饰符 所以我们需要给刚刚的click里使用event.stopProgation()
来阻止
但又有问题来了,我们知道使用v-for需要给每个元素加上key
,但这里我们没有该怎么办?
回到刚刚的代码
let list = [{name:'Dreams'},{name:'Dreams2'}]
const getLiEleArr = h => {
return list.map((item,index) => h('li',{
on:{
'click':(event) => {
console.log(event)
}
},
key:`item_${index}`//这里我们加上key
},item.name))//标签内的内容
}
复制代码
那么到这里,相信大家也对render有了一个大概的了解了。
也就是说我们平时在模板里写的东西其实最后都会被编译为类似上面的语法!
总结
学好render函数在某些情况下可以让我们更好用js去控制一些细节的实现,但我们也会发现,render略显复杂,但是越复杂的东西才越有它存在的意义!
各位大佬,如果发现文中的错误,请指正,我会及时修改!
感谢大佬们能在百忙中能阅读完这篇文章!