vue3的diff算法小结

86 阅读2分钟

简单diff算法

新节点[] 旧节点[]

循环遍历新节点数组,比对vnodetypekey属性,如果相同则可以复用,复用方式为将旧节点oldVNode.el赋值给新节点newVNode.el,同时保存旧节点处于旧列表中的位置作为lastIndex,后续继续进行匹配,

如果匹配到了且小于lastIndex则需要移动到lastIndex对应节点的后面,

如果大于则直接更新lastIndex

如果没有匹配到则表示无法复用,需要创建节点。

遍历完成后,再遍历旧节点数组,查找新节点中有没有对应key值,没有则表示该节点被删除。

很明显,对于下面这种情况简单diff算法将会进行两次真实DOM节点的移动,仍然存在优化空间

n1 -> n2 -> n3  To n3 -> n1 -> n2

双端diff算法

image-20231229110630879.png

while(newStartIdx <= newEndIdx && oldStartIdx <= oldEndIdx){
    //进行双端比较
    ...
}
if (oldEndIdx < oldStartIdx && newStartIdx <= newEndIdx) {
    //添加新节点
    ...
} else if (newEndIdx < newStartIdx && oldStartIdx <= oldEndIdx){
    // 移除操作
    ...
}

快速diff算法

加个一步预处理

从前往后while循环,一一对比key值,如果相同则打补丁,然后继续往后遍历,直到key值不相等,这里用j来记录index
​
从后往前while循环,一一对比key值,如果相同则打补丁,然后继续往前遍历,直到key值不相等,这里分别记录oldEnd和newEnd
​
//旧节点遍历完了,新节点还没有遍历完
if(j > oldEnd && j <= newEnd){
    //新增一系列节点
    while(j <= newEnd){
        ...
    }
}
//新节点遍历完了,旧节点还没有遍历完
else if(j > newEnd && j <= oldEnd){
    //删除一系列节点
    while(j <= oldEnd){
        ...
    }
}
else{
    //对剩余不符合双端匹配的节点进行处理
    //生成一个长度为newEnd - j + 1长度的source数组,存储新节点在旧节点中的位置索引,然后计算出一个最长递增子序列,辅助完成DOM移动操作
    //遍历旧节点数组,匹配新节点中key值对应的节点(这里可以建立索引匹配将复杂度从O(n^2)降到O(n)),进行打补丁操作,更新source数组
    //同时判断是否移动逻辑同简单diff算法,存储最大索引pos,小于则表示需要移动,大于则更新pos
}

image-20231229171423841.png

上图表示索引0,1不需要移动

接下来,处理剩余索引的移动情况

image-20231229171550956.png

如果source数组中存的索引值为-1,表示需要挂载 对应p-7

判断索引2是否等于source数组的值1,不等表示需要移动 对应p-2

如果索引1与source数组对应的值1相等,则表示不需要移动,继续往下遍历 对应p-4

...

遍历结束