关于 vue3 中的 render 方法,你可能不知道这些
不出意外vue3马上就能正式面世了,在面世之后的短期一波时间内,毫无疑问vue3会成为前端热度最高的话题。本着蹭热度要抢早的观念,本人在vue3正式发布之前就开始做这方面的准备,近段时间会一直产出vue3相关的内容,希望能收获观众大佬们的喜欢。
那么闲话不多说,今天想聊的话题是关于vue3中的render
方法的,如果你认真读过rfc那么你可能会有一些印象,但大概率不会专门去关注这块。对于大部分人其实也不会正在接触到render
方法,因为平时我们开发的时候基本直接是 .vue 文件的开发模式,vue-loader
会帮助你编译模板到render
方法。更进一步你可能会使用jsx
来写组件,那么你会更接近render
方法一些,但是最终内容还是会由babel-transform-xxx
来帮你编译,细节你仍然可能是不清楚的。
那么今天我就来为各位扒一扒vue3中的render方法,如果对你有用,还请点赞,关注哦。
既然讲的是render
方法,我们自然不会涉及到模板或者jsx语法,所以接下去所有的展示代码全部都是纯js。我们来看一下最简单的vue组件的写法。
functionComp(props, { slots, attrs }) {return h('div', {id: 'root'}, ['this is content'])
}
这是vue3中的函数组件,在vue3中函数组件不再需要通过functional
声明来实现,只要你的组件是一个函数,那么他就是一个函数组件,反之,只要是一个对象,他就是一个有状态组件。
当然这不是重点,重点是h
函数,换个称呼可能大家更好理解:createElement
。在vue3中,我们需要通过import { h } from 'vue'
来引入这个函数。h
接收三个参数,分别是:
type
,组件类型,字符串代表原生节点,对象或者方法则表示自定义组件props
,组件的属性,通过对象传入slots
,组件插槽内容
前两个很好理解也不是我们今天的重点,所以就不在这里深入了,我们主要关心最后一个参数。在vue3中,对于第三个参数我们有很多种写法,就像茴香豆的茴字(误):
['content']
'content'
() => 'content'
{ default: 'content' }
{ default: () => 'content' }
{ default: () => ['content'] }
那么以上几种写法,最终的结果都是差不多的。你现在是不是很好奇为什么vue3要整这么多种不同的写法,没关系一开始我也是不清楚的,接下去就由我慢慢为你讲解。
第一种和第二种最好理解,其实第二种是第一种的缩写,只要在children
只有一个元素的时候才可以写成这样:
<Comp>content
</Comp>
上面的demo对应到jsx其实就类似酱紫,那么前两种写法就非常好理解了。
那么再来讲讲第三种。我们先来看一下vue rfcs里面的内容:
Inside
Comp
,this.$slots.default
will be a function in both cases and returns the same VNodes. However, the 2nd case will be more performant asthis.msg
will be registered as a dependency of the child component only.
这句话里的demo如下:
h(Comp, [h('div', this.msg)
])
// equivalent:
h(Comp, () => [
h('div', this.msg)
])
简单来说,就是你在写代码的时候不管是前三种里面的哪种写法,在Comp
里面通过this.$slots.default
获取的时候,都是得到一个方法,这个方法调用之后才会返回真正的content
。
但是这里说到了,使用() =>
的方式是性能更好的,原因就是this.msg
的变化只会引起子内容的重新渲染,其实父组件根本不关心this.msg
所以在其更新的时候没必要重新渲染。这涉及到vue的响应式原理,这里就不继续展开,后面有机会再跟大家分享。
那么从这里我们可以得出一个结论,那就是我们正常情况下应该摒弃前面两种写法,而使用第三种方式,因为他们最终表现没有区别,而后者性能更好。
这个结论是正确的,但是又需要注意一点,这种写法只对自定义组件有用,如果是原生节点,是无法展示的,也就是说:
// 可行h(YourComp, () => 'content')
// 不可行
h('div', () => 'content')
可以猜测vue3对于原生节点的渲染是没有slot
的概念,原生节点和自定义组件的渲染方式也是大不相同。不太好解释为什么vue不设计统一的interface,这我也要去探究探究。
前面的说完我们再来说一下后面的三种对象的写法。
一看到对象,我们就应该很快反应过来,唉~,key value
对嘛。然后再联想一下,对的,就是具名插槽。vue里面每个组件是可以有多个插槽的,那我们怎么表示多个插槽呢?自然就是通过不同的key啦:
h(Comp, null, {default: () =>'default',
foo: () =>'foo',
bar: () =>'bar'
})
注意这么些的时候,即便没有props
,第二个参数也是必须的,因为h
函数发现第二个参数如果是对象,那么默认其就是props
。
因为默认只有一个插槽的话,他就是default
插槽,所以只有一个children
节点的时候可以省略对象的写法,也就是我们前面看到的几种写法。
同时这种写法也不能用于原生节点,这是自定义组件才有的特性写法。
另外因为slots
都是函数,所以scopeSlot
就变得非常好理解。在调用该slot
的时候传入参数就行:
// Comph('div', [slots.default('arg')])
这样的话在调用Comp
的地方就可以获取这个参数
h(Comp, (content) => h('div', [content]))
这其实相对于凭空增加一个叫做scopeSlot
更好理解,个人也会更推崇这种用法。
探索
在深入理解了vue3中关于slot的用法之后,我不禁想到,既然slot就是函数,那我直接通过props传递函数是不是也可以呢?于是我试了一下:
// Comph('div', [props.renderA()])
// App
h(Comp, {renderA: () => h('span', 'content')})
发现这是可行的,而且是完全可以触发reactive的重新渲染的。
当然这只是初步探索,我目前还不确定vue内部对于slot有没有特殊处理,如果没有什么特殊处理,那么renderProps的用法在vue中就是完全可行的,个人表示非常期待。
好了,今天的分享就到这里,我会尽量更多得为大家分享有意义的内容。
以上是 关于 vue3 中的 render 方法,你可能不知道这些 的全部内容, 来源链接: utcz.com/a/29223.html