vue-vdom-diff
分为以下几个流程:
newVNode === undefined && oldVNode !== undefined
直接做移除操作。oldVNode === undefined
做新增操作。isSomeVNode(oldVNode,newVNode) === true
进入children
的patch
流程。- 进入更新当前
VNode
流程。
vnode updateChildren 流程
vnode children
的更新流程跟数组操作类似。
-
oldVnode = [a, b, c, d]; newVnode = [c, d];
-
oldVnode = [a, b, c, d]; newVnode = [e, f, a, b, c, d];
-
oldVnode = [a, b, c, d]; newVnode = [a, b];
-
oldVnode = [a, b, c, d]; newVnode = [a, b, c, d, e, f];
-
oldVnode = [a, b, c, d]; newVnode = [a, b, e, f, c, d];
-
oldVnode = [a, b, c, d]; newVnode = [a, d];
以上是分成的原子操作,但是实际生产中可能发生的情况更加复杂,是上面的任意组合。
patchChildren 比较流程
循环判断条件:oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx
,即还没有遍历完oldCh
或者 newCh
,只要有一个遍历完了,就会跳出循环。
-
首先判断旧的开始节点
oldStartVnode
是否为空1.1 为空的话,将
oldStartIdx
向后偏移一位,并重新对oldStartVnode = oldCh[++oldStartIdx]
进行赋值,满足循环的情况下重新进入步骤1
。 -
判断旧的结束节点
oldEndVnode
是否为空2.1 为空的话,将
oldEndIdx
向前偏移一位,并重新对oldEndVnode = oldCh[--oldEndIdx]
进行赋值,满足循环的情况下重新进入步骤1
。 -
对比新旧的开始节点
sameVnode(oldStartVnode, newStartVnode)
是否是同一个VNode
3.1 是同一个
VNode
,进入他们的children patch
流程。3.2 将
oldStartIdx
、newStartIdx
继续向后偏移一位,并对oldStartVnode
、newStartVnode
进行重新赋值,满足循环的情况下重新进入步骤1
。 -
对比新旧的结束节点
sameVnode(oldEndVnode, newEndVnode)
是否是同一个VNode
4.1 是同一个
VNode
,进入他们的children patch
流程。4.2 将
oldEndIdx
、newEndIdx
继续向后偏移一位,并对oldEndVnode
、newEndVnode
进行重新赋值,满足循环的情况下重新进入步骤1
。 -
对比旧的开始节点、新的结束节点
sameVnode(oldStartVnode, newEndVnode)
是否是同一个VNode
5.1 是同一个
VNode
,进入他们的children patch
流程。5.2 如果不是在
<transition-group>
组件内用的话,将oldStartVnode.elm
移动oldEndVnode.elm.nextSibling
的位置。5.3
oldStartIdx
向后偏移一位,newEndIdx
向前偏移一位。5.4 并对
oldStartVnode
、newEndVnode
进行重新赋值,满足循环的情况下重新进入步骤1
。 -
对比旧的结束节点、新的开始节点
sameVnode(oldEndVnode, newStartVnode)
是否是同一个VNode
6.1 是同一个
VNode
,进入他们的children patch
流程。6.2 如果不是在
<transition-group>
组件内用的话,将oldEndVnode.elm
移动oldStartVnode.elm.nextSibling
的位置。6.3
oldEndIdx
向前偏移一位,newStartIdx
向后偏移一位。6.4 并对
oldEndVnode
、newStartVnode
进行重新赋值,满足循环的情况下重新进入步骤1
。 -
建立
oldKeyToIdx
这个map
,数据结构如下:interface OldKeyToIdxMap { [key: string]: number; }
7.1 根据
newStartVnode.key
去oldKeyToIdx
找对于旧的VNode
的idxInOld
。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
。
不满足循环条件。
oldStartIdx > oldEndIdx
,进入创建新增的VNode
流程。newStartIdx > newEndIdx
,进入移除oldCh
里面的VNode
流程。