一、Diff 算法
Vue的Diff算法是一种通过同层的树节点进行比较的高效算法,避免对树的逐层遍历,减少时间复杂度
特点
- 只会同级比较,不跨级,广度优先遍历
- diff比较循环两边往中间靠拢
二、Vue Diff算法
Vue的虚拟dom diff核心在于patch过程,核心算法采用了双指针算法。
2.1首先将新旧VNode进行开始位置和结束位置做标记
let oldStartIndex = 0;
let oldEndIndex = oldChildren.length-1;
let oldStartVnode = oldChildren[0];
let oldEndVnode = oldChildren[oldEndIndex];
let newStartIndex = 0;
let newEndIndex = newChildren.length-1;
let newStartVnode = newChildren[0];
let newEndVnode = newChildren[newEndIndex];
2.2标记好节点位置,进行循环处理节点
- 如果当前oldStartVnode和newStartVnode节点相同,直接用新节点复用老节点,进行patchVnode复用,更新oldStartVnode、newStartVnode, oldStartIndex++和newStartIndex++;
- 如果当前oldEndVnode和newEndVnode节点相同,直接用新节点复用老节点,进行patchVnode复用,更新oldEndVnode、newEndVnode、oldEndIndex--和newEndIndex--;
- 如果当前oldStartVnode和newEndVnode节点相同,直接用新节点复用老节点,进行patchVnode复用,将老节点移动到oldEndVnode节点之后,更新oldStartVnode、newEndVnode、oldStartIndex++和newEndIndex--
- 如果当前oldEndVnode和newStartVnode节点相同,直接用新节点复用老节点,进行patchVnode复用,更新oldStartVnode、newStartVnode、oldEndIndex--和newStartIndex++;
- 如果都不满足上面过程,则进行key对比。满足条件进行patchVnode,并将dom移动到oldStartVnode之前。没有找到则重新创建
2.3递归处理
三、Vue Diff图解
- 第一步:创建四个指针,分别为旧VNode的开始指针和结束指针、新VNode的开始和结束指针
- 第二步:先比较旧VNode的开始指针和新VNode的开始指针,即A和E,发现不是同一个节点
- 第三步:再比较旧VNode的结束指针和新的VNode的结束指针,即D和F,依然不是相同的节点
- 第四步:再比较旧VNode的开始指针和新VNode的结束指针,即A和F,不是相同的节点
- 第五步:再比较旧VNode的结束指针和新VNode的开始指针,即E和D,不是相同的节点
- 第六步:通过上述四种比对方式都不是相同的节点,下面就在旧VNode节点中查找是否与E节点相同的节点
- 第七步:发现旧VNode节点中没有E节点,那么就会在旧VNode前插入一个新的E节点
- 第八步:第一个节点操作完成后,指针后移,继续进行比较,重复第二到第七步,结果为:新增/删除/移动
- 第九步:当找到相同的节点时,会通过patchVnode方法进行这两个节点更细致的Diff算法
缺点
在vue2的dom diff过程中,是一边diff 一边操作dom的,其实这样子很耗性能,所以vue3的dom diff完全重写了(这太操蛋了。。。),后面会写一篇vue3dom diff原理的文章
总结
每次Diff都会调用updateChildren方法来比较,就这样子层层递归下去,直到将旧VNode和新VNode中的所有子节点比对完。是一个深度递归遍历的过程,同时也在一边去操作dom。