1. 简介
Diff算法是一种通过同层的树节点进行对比的高效算法。例如我们提交 pr 或 mr时,会对比当前代码的改动。而Vue的diff算法是对vdom的一种对比。
Diff的整体策略: 深度优先,同层渲染。
2. Vue2 Diff
2.1 单节点对比
/*
判断两个VNode节点是否是同一个节点,需要满足以下条件
key相同
tag(当前节点的标签名)相同
isComment(是否为注释节点)相同
是否data(当前节点对应的对象,包含了具体的一些数据信息,是一个VNodeData类型,可以参考VNodeData类型中的数据信息)都有定义
当标签是<input>的时候,type必须相同
*/
function sameVnode (a, b) {
return (
a.key === b.key &&
a.tag === b.tag &&
a.isComment === b.isComment &&
isDef(a.data) === isDef(b.data) &&
sameInputType(a, b)
)
}
2.2 多节点对比
双端比较:
while循环2个列表,利用四个指针指向双列表的前后,假设一致则复用,不一致则下标+1继续进行对比。
1.oldStartIndex.key === newStartIndex.key
2.oldStartIndex.key === newEndIndex.key
3.oldEndIndex.key === newStartIndex.key
4.oldEndIndex.key === newEndIndex.key
5.查找旧列表的缓存映射
5.1 有`key`相同则复用,设置`oldNode = undefined,` 移动`newStartIndex`
5.2 找不到则新建一个放在最前面,移动`newStartIndex`
6.循环走完了
6.1 `oldEndIndex` < `oldStartIndex` 判断新列表是否还有节点,有的话则按位置插入节点就行。
6.2 `newEndIndex` < `newStartIndex` 判断旧列表是否还有节点,有的话则删除
3. Vue3 Diff
1.前-前对比
2.后-后对比
3.最长递增子序列: 较少DOM的移动、达到最小的DOM操作
例如数据 [3, 5, 7, 1, 2, 8]的最长递增子序列就是[3, 5, 7, 8]
例子:
1.前-前对比 -> A B
2.后-后对比 -> G
3. 剩余节点 F C D E H
通过 newIndexToOldIndexMap 拿到oldChildren 中对应的 index [5, 2, 3, 4, -1](-1表示没有,要新增)
计算最长递增子序列 得出[2, 3, 4]表示 C D E 不需要变化
剩余的节点,根据index进行新增、删除、移动