通俗易懂的vue2 dom-diff

506 阅读3分钟

一、Diff 算法

Vue的Diff算法是一种通过同层的树节点进行比较的高效算法,避免对树的逐层遍历,减少时间复杂度

特点

  1. 只会同级比较,不跨级,广度优先遍历
  2. 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。