
diff 算法是一种通过同层的树节点进行比较的高效算法
其有两个特点:
diff 算法在很多场景下都有应用,在 vue 中,作用于虚拟 dom 渲染成真实 dom 的新旧 VNode 节点比较
diff整体策略为:深度优先,同层比较
下面举个vue通过diff算法更新的例子:
新旧VNode节点如下图所示:

第一次循环后,发现旧节点D与新节点D相同,直接复用旧节点D作为diff后的第一个真实节点,同时旧节点endIndex移动到C,新节点的 startIndex 移动到了 C

第二次循环后,同样是旧节点的末尾和新节点的开头(都是 C)相同,同理,diff 后创建了 C 的真实节点插入到第一次创建的 D 节点后面。同时旧节点的 endIndex 移动到了 B,新节点的 startIndex 移动到了 E

第三次循环中,发现E没有找到,这时候只能直接创建新的真实节点 E,插入到第二次创建的 C 节点之后。同时新节点的 startIndex 移动到了 A。旧节点的 startIndex 和 endIndex 都保持不动

第四次循环中,发现了新旧节点的开头(都是 A)相同,于是 diff 后创建了 A 的真实节点,插入到前一次创建的 E 节点后面。同时旧节点的 startIndex 移动到了 B,新节点的 startIndex 移动到了 B

第五次循环中,情形同第四次循环一样,因此 diff 后创建了 B 真实节点 插入到前一次创建的 A 节点后面。同时旧节点的 startIndex 移动到了 C,新节点的 startIndex 移动到了 F

新节点的 startIndex 已经大于 endIndex 了,需要创建 newStartIdx 和 newEndIdx 之间的所有节点,也就是节点F,直接创建 F 节点对应的真实节点放到 B 节点后面

当数据发生改变时,set方法会调用Dep.notify通知所有订阅者Watcher,订阅者就会调用patch给真实的DOM打补丁,更新相应的视图
源码位置:src/core/vdom/patch.js
patch函数前两个参数位为oldVnode 和 Vnode ,分别代表新的节点和之前的旧节点,主要做了四个判断:
destory钩子createElmsameVnode 判断节点是否一样,一样时,直接调用 patchVnode 去处理这两个节点下面主要讲的是patchVnode部分
patchVnode主要做了几个判断:
dom的文本内容为新节点的文本内容DOM,并且添加进父节点DOM 删除子节点不完全一致,则调用updateChildren
while循环主要处理了以下五种情景:
VNode 节点的 start 相同时,直接 patchVnode ,同时新老 VNode 节点的开始索引都加 1VNode 节点的 end相同时,同样直接 patchVnode ,同时新老 VNode 节点的结束索引都减 1VNode 节点的 start 和新 VNode 节点的 end 相同时,这时候在 patchVnode 后,还需要将当前真实 dom 节点移动到 oldEndVnode 的后面,同时老 VNode 节点开始索引加 1,新 VNode 节点的结束索引减 1VNode 节点的 end 和新 VNode 节点的 start 相同时,这时候在 patchVnode 后,还需要将当前真实 dom 节点移动到 oldStartVnode 的前面,同时老 VNode 节点结束索引减 1,新 VNode 节点的开始索引加 1从旧的 VNode 为 key 值,对应 index 序列为 value 值的哈希表中找到与 newStartVnode 一致 key 的旧的 VNode 节点,再进行patchVnode ,同时将这个真实 dom 移动到 oldStartVnode 对应的真实 dom 的前面
调用 createElm 创建一个新的 dom 节点放到当前 newStartIdx 的位置
watcher就会调用patch给真实的DOM打补丁isSameVnode进行判断,相同则调用patchVnode方法patchVnode做了以下操作:
dom,称为elel文本节点设置为Vnode的文本节点oldVnode有子节点而VNode没有,则删除el子节点oldVnode没有子节点而VNode有,则将VNode的子节点真实化后添加到elupdateChildren函数比较子节点updateChildren主要做了以下操作:
设置新旧VNode的头尾指针
新旧头尾指针进行比较,循环向中间靠拢,根据情况调用patchVnode进行patch重复流程、调用createElem创建一个新节点,从哈希表寻找 key一致的VNode 节点再分情况操作