vue-vdom-diff

分为以下几个流程:

  1. newVNode === undefined && oldVNode !== undefined 直接做移除操作。
  2. oldVNode === undefined 做新增操作。
  3. isSomeVNode(oldVNode,newVNode) === true 进入childrenpatch流程。
  4. 进入更新当前VNode流程。

vnode updateChildren 流程

vnode children的更新流程跟数组操作类似。

  1. 类似shift 移除前面的 vnode

    oldVnode = [a, b, c, d];
    newVnode = [c, d];
    
  2. 类似unshift 在最前面插入vnode

    oldVnode = [a, b, c, d];
    newVnode = [e, f, a, b, c, d];
    
  3. 类似pop 移除后面的vnode

    oldVnode = [a, b, c, d];
    newVnode = [a, b];
    
  4. 类似push 在最后面插入vnode

    oldVnode = [a, b, c, d];
    newVnode = [a, b, c, d, e, f];
    
  5. 类似insert 对中间部分新增

    oldVnode = [a, b, c, d];
    newVnode = [a, b, e, f, c, d];
    
  6. 类似del 对中间部分删除

    oldVnode = [a, b, c, d];
    newVnode = [a, d];
    

以上是分成的原子操作,但是实际生产中可能发生的情况更加复杂,是上面的任意组合。

patchChildren 比较流程

循环判断条件:oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx,即还没有遍历完oldCh 或者 newCh,只要有一个遍历完了,就会跳出循环。

  1. 首先判断旧的开始节点oldStartVnode是否为空

    1.1 为空的话,将oldStartIdx向后偏移一位,并重新对oldStartVnode = oldCh[++oldStartIdx]进行赋值,满足循环的情况下重新进入步骤1

  2. 判断旧的结束节点oldEndVnode是否为空

    2.1 为空的话,将oldEndIdx向前偏移一位,并重新对oldEndVnode = oldCh[--oldEndIdx]进行赋值,满足循环的情况下重新进入步骤1

  3. 对比新旧的开始节点sameVnode(oldStartVnode, newStartVnode)是否是同一个VNode

    3.1 是同一个VNode,进入他们的children patch流程。

    3.2 将oldStartIdxnewStartIdx继续向后偏移一位,并对oldStartVnodenewStartVnode进行重新赋值,满足循环的情况下重新进入步骤1

  4. 对比新旧的结束节点sameVnode(oldEndVnode, newEndVnode)是否是同一个VNode

    4.1 是同一个VNode,进入他们的children patch流程。

    4.2 将oldEndIdxnewEndIdx继续向后偏移一位,并对oldEndVnodenewEndVnode进行重新赋值,满足循环的情况下重新进入步骤1

  5. 对比旧的开始节点、新的结束节点sameVnode(oldStartVnode, newEndVnode)是否是同一个VNode

    5.1 是同一个VNode,进入他们的children patch流程。

    5.2 如果不是在<transition-group>组件内用的话,将oldStartVnode.elm移动oldEndVnode.elm.nextSibling的位置。

    5.3 oldStartIdx向后偏移一位,newEndIdx向前偏移一位。

    5.4 并对oldStartVnodenewEndVnode进行重新赋值,满足循环的情况下重新进入步骤1

  6. 对比旧的结束节点、新的开始节点sameVnode(oldEndVnode, newStartVnode)是否是同一个VNode

    6.1 是同一个VNode,进入他们的children patch流程。

    6.2 如果不是在<transition-group>组件内用的话,将oldEndVnode.elm移动oldStartVnode.elm.nextSibling的位置。

    6.3 oldEndIdx向前偏移一位,newStartIdx向后偏移一位。

    6.4 并对oldEndVnodenewStartVnode进行重新赋值,满足循环的情况下重新进入步骤1

  7. 建立oldKeyToIdx这个map,数据结构如下:

    interface OldKeyToIdxMap {
      [key: string]: number;
    }
    

    7.1 根据newStartVnode.keyoldKeyToIdx找对于旧的VNodeidxInOld

    7.1.1 未找到,根据newStartVnode重新创建元素。

    7.1.2 取出并赋值vnodeToMove = oldCh[idxInOld]

    7.1.2.1 判断sameVnode(vnodeToMove, newStartVnode)

    7.1.2.1.1 为true,进入他们的children patch流程。

    7.1.2.1.2 将旧的赋值成oldCh[idxInOld] = undefined

    7.1.2.1.2 并移动nodeOps.insertBefore(parentElm, vnodeToMove.elm, oldStartVnode.elm)

    7.1.3 newStartIdx向后偏移一位,满足循环的情况下重新进入步骤1

不满足循环条件。

  1. oldStartIdx > oldEndIdx,进入创建新增的VNode流程。
  2. newStartIdx > newEndIdx,进入移除oldCh里面的VNode流程。