持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第4天,点击查看活动详情
前言
Vue3对Vnode(虚拟dom)的结构做了调整,以适应编译器的优化策略,相对应的patch算法也会利用这些变化提高运行速度
新的Vnode结构
要了解新的Vnode结构,首先需要搞清楚以下的问题:
Vue3的虚拟Dom结构是在什么时候第一次生成
const vnode = createVNode({
rootComponent as ConcreteComponent,
rootProps
})
vnode.appContext = context
在第一次被挂载的时候生成,首次挂载创建根组件对应的Vnode,在后续需要再次执行该方法内部的渲染函数,再次渲染进行细化
虚拟Dom内部结构变化
- Vue2中
Vnode的结构children只能是数组或者是空字符串,Vue3中的children即可以是数组也可以是文本
在Vue3中如果
children是数组,即可代表内的每一个对象是一个元素,如果是字符串即可代表当前这个节点是文本。
- 在编译过程中如果打开了
Block,当前的节点如果有动态的部分,则需要存储在dynamicChildren中 - 动态属性,
patchFlag补丁标记,保存将来有哪些内容是动态的(比如:属性、值、样式) shapeFlag标明当前节点基础的类型,可以清晰的返回当前的节点是什么类型,从而做不同的流程(switch语句是根据它来进行精准判断)- type表明当前节点类型,在Vue2它是一个字符串(表明当前节点的名称),在Vue3中,它有两种类型,第一种是组件的配置对象,第二种是
Symbol(Fragment)普通节点类型
过程
创建Vnode
在mount()执行时,创建根组件Vnode
mount(rootContainer:HostElement,isHydrate:boolean):
if(!isHydrate){
const vnode = createVNode({})
}
渲染Vnode
render(vnode,rootContainer)方法将创建的vnode渲染到根容器上
初始化patch
传入oldVnode为null,初始path为创建行为
patch(container._vnode || null,vnode,container)
使用mountConponent将n2转换为dom
mountComponent(n2,container,anchr,parentComponent,parentSuspense,isSVG,optimized)
创建一个渲染副作用,执行render,获得Vnode之后,在执行patch转换为dom
setupRenderEffect(
instance,initialVNode,container,anchor,parnetSuspense,isSVG,optimized
)
它在初始化阶段,核心任务是执行instance的render函数获取subTree
const subTree = (instance.subTree = renderComponentRoot(instance))
最后patch这个subTree
patch(subTree)
更新阶段
更新阶段,patch函数对比新旧Vnode,得出dom操作内容,componentEffect中会调用patch,并传入新旧两个Vnode
patch(subTree,prevTree,nextTree)
多个子元素更新
如果同时出现多个子元素,比如使用v-for时的情况,则需要重排操作,使用patchChildren更新
patchChildren(fragmentEndAnchor)
如果设置了key的情况下,则需要使用patchKeyedChildren
patchKeyedChildren(fragmentEndAnchor)