vue源码记录:diff算法

207 阅读2分钟

diff 算法总结

简单来说,diff 算法有以下过程

  • 同级比较,再比较子节点
  • 先判断一方有子节点一方没有子节点的情况(如果新的 children 没有子节点,将旧的子节点移除)
  • 比较都有子节点的情况(核心 diff)
  • 递归比较子节点

patch 算法:

oldVnode: 旧虚拟节点或 DOM 节点(首次渲染时)

vnode: 新虚拟节点

  1. 若 vnode 不存在,直接销毁 oldVnode;
  2. 若 oldVnode 不存在,直接根据 vnode 创建 DOM 节点;
  3. 若 oldVnode 和 vnode 都存在,则判断 oldVnode 是否为真实的 DOM 节点:
    1. 若 oldVnode 不是真实 DOM 节点且根据 sameVnode 方法判断 oldVnode 和 vnode 是否相同,相同则直接进行 patchVnode 对比。
    2. 若 oldVnode 是真实 DOM(首次渲染),并创建一个空节点替换 oldVnode。根据 vnode 创建 DOM 节点。

patchVnode 算法:

  1. oldVnode === vnode,直接返回,流程结束。
  2. oldVnode !== vnode,若都是静态节点,且 key 相同,vnode 是克隆的或者使用 v-once,则直接将 oldVnode 的组件实例赋值给 vnode,流程结束。
  3. 当 vnode 不是文本节点时,
    1. 当 oldVnode 和 vnode 都存在子节点时,使用 updateChildren() 对比子节点;
    2. 若 oldVnode 无子节点,若 oldVnode 是文本节点,则将文本置空,并根据子虚拟节点创建 DOM 节点;
    3. 若 vnode 无子节点,则直接移除 oldVnode 的子节点;
    4. 若 oldVnode 是文本节点,则直接将 oldVnode 的文本置空;
  4. 当 vnode 是文本节点,且 oldVnode.text !== vnode.text 时,直接替换 text

updateChildren 算法:

从 oldVnode 和 vnode 的 children 双端进行比较,借助 key 值找到可复用的节点,再进行相关操作。

递归比较子节点:

  1. sameVnode(oldStartVnode, newStartVnode):首部对比,相同则 patchNode ,比较下一个首部;
  2. sameVnode(oldEndVnode, newEndVnode):尾部对比,相同则 patchNode ,比较下一个尾部;
  3. sameVnode(oldStartVnode, newEndVnode):首尾对比,相同则 patchNode ,并将 oldStartVnode 对应的 DOM 节点移动到最后,比较下一个首尾;
  4. sameVnode(oldEndVnode, newStartVnode):尾首对比,相同则 patchNode ,并将 oldEndVnode 对应的 DOM 节点移动到最前,比较下一个尾首;
  5. 其他,若 newStartVnode 的 key 在 oldVnode 的 children 中找不到,则根据 newStartVnode 直接创建 DOM 节点;若找到相同的 key,且根据 sameVnode 方法判断,相同则 patchNode ,将 oldVnode 的 child 赋值为 undefined,并将 newStartVnode 对应的 DOM 节点移动到 oldStartVnode 对应的 DOM 节点前;若 key 相同但元素不同,则直接创建 DOM 节点。

若 vnode.children > oldVnode.children,则无需对比,直接根据多余子节点创建 DOM 节点 若 vnode.children < oldVnode.children,则无需对比,直接移除 oldVnode 的多余子节点。