一、前言
Vue作为现阶段的主流框架之一,许多项目都使用它来搭建项目前端模块。本文主要介绍Vue中页面渲染时发生的虚拟DOM比对所运用的算法,使大家更加了解Vue。
二、简介
MVVM,即model、view、view-model,业务层、视图层以及两者的绑定层,Vue的设计就是参考了该架构。
<h1>{
{title}}</h1>
<script lang='ts' setup>
const title = 'this is title'
</script>
在Vue中,<h1>
标签就是由view层管理,而const title = 'this is title'
就是由model层管理的,view-model层要做的就是使这两个数据能保持同步。而要更新标签数据就必不可少的需要重新渲染它。
由于响应式数据改变的渲染是有老vnode
存在的,所以Vue就需要最大限度的复用已经创建的DOM元素,而复用的前提就是通过新老vnode
进行比对,找出需要更新的内容,最小限度的进行替换。更新过程:
-
修改data触发setter,调用Dep.notify通知所有订阅者Watcher;
-
重新执行render函数,生成
newVnode
; -
执行
patch(vnode,newVnode)
,diff
算法得出最终需要更新的节点;
三、diff算法
1.什么是diff算法
diff算法就是进行虚拟节点比对,并返回一个patch对象,用来存储两个节点不同的地方,最后用patch记录的消息去局部更新Dom。
换句听得懂的就是
diff的过程就是调用名为patch的函数,比较新旧节点,一边比较一边给真实的DOM打补丁
2.步骤
-
当状态变更时,重新构造一颗由JS对象结构表示的DOM树;
-
将新的DOM树与旧的DOM树进行比对,记录其差异;(diff)
-
将记录的差异应用到真实的DOM中;(patch)
3.具体方法
基本策略
-
同层比较
-
两边向中间进行比较
举例说明
新旧vnode
如下所示:
第一次循环:oldVnode
中的startIndex
所指向的A
与endIndex
所指向的D
和newVnode
中的startIndex
所指向的D
以及newVnode
中的endIndex
所指向的F分别进行比较(以下比较方式都相同,略过),发现具有相同节点D
,直接复用D
作为真实节点,oldVnode
中的endIndex
向前一位,newVnode
中的startIndex
向后一位。
第二次循环:A
、C
与C
、F
比较,得到C
相同,复用节点C
,指针移动。
第三次循环:A
、B
与E
、F
比较,发现没有可复用节点,创建新的节点E
,newVnode
的startIndex
向后移动一位。
第四次循环:A
、B
与B
、F
比较,得到B相同,复用节点B
,指针移动。
第五次循环:A
与A
、F
比较,得到A相同,复用节点A
,指针移动后发现oldVnode
中头指针大于尾指针,则创建newVnode
头尾指针间的所有节点(F
),跳出循环。
四、小结
-
当数据发生改变时,订阅者watcher就会调用patch给真实的DOM打补丁
-
通过isSameVnode进行判断,相同则调用patchVnode方法
-
patchVnode做了以下操作:
-
找到对应的真实dom,称为el
-
如果都有都有文本节点且不相等,将el文本节点设置为
Vnode
的文本节点 -
如果
oldVnode
有子节点而VNode
没有,则删除el子节点 -
如果
oldVnode
没有子节点而VNode
有,则将VNode
的子节点真实化后添加到el -
如果两者都有子节点,则执行updateChildren函数比较子节点
-