赞
踩
注:本文只是介绍实现原理,并非常规源码解读
众所周知,Vue中的tamplate会被编译为渲染函数,渲染函数执行生成DOM子树,然后根据组件实例的状态执行mount或patch,如果patch,diff算法会对比两颗子树并进行更新。
观察这段模板:
<div>
<div>HelloVue!</div>
<p>{
{ text }}</p>
</div>
这段模板对应的虚拟DOM如下:
{
tag:'div',
children:[
{tag:'div',children:'HelloVue'},
{tag:'p,children: _context.text}
]
}
传统diff算法对比DOM树更新流程:
1.对比外层div,它的props,和它的子节点,
2.对比内层div,它的props,和它的文本子节点
3.对比内层p,它的props,和它的文本子节点,
可以看到diff算法做了很多没啥意义的对比,这段代码其实只有text绑定了渲染上下文中的内容,其余内容都是静态内容,所以虚拟DOM只需要对比p标签的文本节点就可以完成内容更新,从而提升性能。但是从上面的虚拟DOM可以看到,运行时其实没办法知道哪个文本节点是动态节点,children数组只是两个普通的vnode
还是相同的一段模板:
<div>
<div>HelloVue!</div>
<p>{
{ text }}</p>
</div>
我们很容易就能看出来只有p标签含有动态内容,Vue3的模板经过编译后,可以把从模板中提取到这个信息附着到对应的vnode上,可以看到编译后的vnode中p标签多了一个patchFlag属性。
{
tag:'div',
children:[
{tag:'div',children:'HelloVue'},
//patchFlag等于1说明这个节点的textContent是动态内容
{tag:'p,children: _context.text,patchFlag: 1 }
]
}
patchFlag属性可以让运行时知道这个vnode是一个含有动态内容的vnode(后面简称动态vnode),而且根据这个属性的值还可以精准的判断标签的动态部分到底在哪。所以可以在创建VNode的时候,把Vnode子节点中的被patchFlag标记的动态vnode提取出来(这个提取过程后面会讲到),保存在它自己的dynamicChildren数组内。我们把含有这个dynamicChildren数组的VNode就叫做一个block,block不只会提取到children的动态VNode,还可以提取到所有子代动态VNode。
当然vnode也并不是凭空产生的,而是由render函数产生,但是render函数不会直接返回vnode,要通过辅助函数createVNode()
createVNode的实现大概如下:
function createVNode(tag,prop
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。