VUE 子节点diff算法
第一步创建变量
let oldStartIdx = 0;
let newStartIdx = 0;
let oldEndIdx = oldCh.length - 1;
let oldStartVnode = oldCh[0];
let oldEndVnode = oldCh[oldEndIdx];
let newEndIdx = newCh.length - 1;
let newStartVnode = newCh[0];
let newEndVnode = newCh[newEndIdx];
let oldKeyToIdx, idxInOld
- 创建新旧子起始和结束四个索引oldStartIdx、newStartIdx、oldEndIdx、oldEndVnode
- 获取新旧起始和结束四个子Vnode
- oldKeyToIdx用来存储oldStartIdx和oldEndIdx之间老的vnode的key和index
{
oldCh[oldStartIdx].key: oldStartIdx
...
oldCh[oldEndIdx].key: oldEndIdx
}
4.idxInOld用来获取newCh[newStartIdx]这个新的vode的key在oldKeyToIdx里的索引
第二步进行while循环
- 首先是旧的第一个Vode和新的第一个Vnode进行比较,如果新的Vnode的key和tag都一样,说明他们是同一个元素,那就将他们进行深度比较patchVnode,并且将oldStartIdx+1,newStartIdx+1,并且将oldStartVnode和newStartVnode赋值为下一个child
if (sameVnode(oldStartVnode, newStartVnode)) {
patchVnode(
oldStartVnode,
newStartVnode,
insertedVnodeQueue,
newCh,
newStartIdx
);
oldStartVnode = oldCh[++oldStartIdx];
newStartVnode = newCh[++newStartIdx];
}
- 如果旧的第一个Vode和新的第一个Vnode不是同一个元素,那就将旧的最后一个Vnode和新的最后一个Vnode进行比较,如果他们是同一个元素,oldEndVnode-1,newEndIdx-1,并且将oldStartVnode和newStartVnode赋值为上一个child
if (sameVnode(oldEndVnode, newEndVnode)) {
patchVnode(
oldEndVnode,
newEndVnode,
insertedVnodeQueue,
newCh,
newEndIdx
);
oldEndVnode = oldCh[--oldEndIdx];
newEndVnode = newCh[--newEndIdx];
}
- 如果旧的第一个Vnode和新的第一个Vnode不是同一个元素,那么就会将旧的第一个Vnode和新的最后一个Vnode进行比较,如果他们是同一个元素,就将oldEndVnode对应的真实元素移动到的oldEndVnode对的元素的后面去,并且将oldStartIdx+1,newEndIdx-1,oldStartVnode赋值为下一个Vnode,newEndVnode赋值为上一个Vnode
f (sameVnode(oldStartVnode, newEndVnode)) {
// Vnode moved right
patchVnode(
oldStartVnode,
newEndVnode,
insertedVnodeQueue,
newCh,
newEndIdx
);
canMove &&
nodeOps.insertBefore(
parentElm,
oldStartVnode.elm,
nodeOps.nextSibling(oldEndVnode.elm)
);
oldStartVnode = oldCh[++oldStartIdx];
newEndVnode = newCh[--newEndIdx];
}
- 如果旧的第一个Vnode和新的最后一个Vnode不是同一个元素,那么就会oldEndVnode和newStartVnode进行比较,如果他们是同一个元素,那么就将oldEndVnode对应的元素移动oldStartVnode对应元素的前面,并将oldEndIdx-1,newStartIdx+1,oldEndVnode赋值为上一个Vnode,newStartVnode赋值为下一个Vnode
if (sameVnode(oldEndVnode, newStartVnode)) {
// Vnode moved left
patchVnode(
oldEndVnode,
newStartVnode,
insertedVnodeQueue,
newCh,
newStartIdx
);
canMove &&
nodeOps.insertBefore(parentElm, oldEndVnode.elm, oldStartVnode.elm);
oldEndVnode = oldCh[--oldEndIdx];
newStartVnode = newCh[++newStartIdx];
}
- 如果以上都不成立,
- 则会获取oldKeyToIdx,并且获取newStartVnode.key在oldVnode集合中所在索引位置
- 如果idxInOld不存在,这创建一个新的元素
- 如果idxInOld存在,则会比较两个newStartVnode和oldCh[idxInOld]这两个Vnode,如果他们是同一个元素,就将oldCh[idxInOld]对应元素移动到oldStartVnode对应的元素前面,不是同一个元素,则会创建一个新的元素,然后将oldCh[idxInOld]设置为空,再将newStartId+1、newStartVnode设置为下一个Vnode
if (isUndef(oldKeyToIdx))
oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx);
idxInOld = isDef(newStartVnode.key)
? oldKeyToIdx[newStartVnode.key]
: findIdxInOld(newStartVnode, oldCh, oldStartIdx, oldEndIdx);
if (isUndef(idxInOld)) {
// New elementxx
createElm(
newStartVnode,
insertedVnodeQueue,
parentElm,
oldStartVnode.elm,
false,
newCh,
newStartIdx
);
} else {
vnodeToMove = oldCh[idxInOld];
if (sameVnode(vnodeToMove, newStartVnode)) {
patchVnode(
vnodeToMove,
newStartVnode,
insertedVnodeQueue,
newCh,
newStartIdx
);
oldCh[idxInOld] = undefined;
canMove &&
nodeOps.insertBefore(
parentElm,
vnodeToMove.elm,
oldStartVnode.elm
);
} else {
// same key but different element. treat as new element
createElm(
newStartVnode,
insertedVnodeQueue,
parentElm,
oldStartVnode.elm,
false,
newCh,
newStartIdx
);
}
}
newStartVnode = newCh[++newStartIdx];
}
重复上面五个步骤,直至oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx条件不成立
while循环结束
if (oldStartIdx > oldEndIdx) {
refElm = isUndef(newCh[newEndIdx + 1]) ? null : newCh[newEndIdx + 1].elm;
addVnodes(
parentElm,
refElm,
newCh,
newStartIdx,
newEndIdx,
insertedVnodeQueue
);
} else if (newStartIdx > newEndIdx) {
removeVnodes(oldCh, oldStartIdx, oldEndIdx);
}
如果oldStartIdx大于oldEndIdx,说明老的Vnode已经遍历完了,但是新的还没遍历万,这时就需要批量创建插入了 如果newStartIdx大于newEndIdx,则说明新的Vnode已经遍历完了,但是老的Vnode还没遍历完,这时就需批量进行删除