- 获取新旧vnode
旧的vnode通过实例的_vnode属性获取
新的vnode通过实例的$render()方法生成,同时使用另外一个变量引用instance._vnode,然后将新的vnode赋值给instance._vnode - 对比新旧vnode
- 调用patch(oldVnode, newVnode)
- 如果sameVnode(oldVnode, newVnode),则调用patchVnode(oldVnode, newVnode)
- 根据newVnode是否为text类型,以及是否有children来进一步分析
- newVnode为文本节点(newVnode.text !== undefined),直接oldVnode.elm(真实dom元素).innerHtml = newVnode.text
- newVnode不为文本节点,
- 新旧Vnode都有children,调用updateChildren(parentElm, oldCh, newCh)
- newVnode有children(同时也意味着oldVnode没有children),将oldVnode置空,再使用addVnodes添加newVnode的children。
- oldVnode有children(同时也意味着newVnode没有children),使用removeVnodes移除旧的
- 新旧Vnode都没有children,且旧的为text,直接oldVnode.elm(真实dom元素).innerHtml = ‘’
- updateChildren(parentElm, oldCh, newCh)方法使用while来比较数组下面的新旧节点(这里递归使用了patchVnode)
- oldCh第一个Vnode和newCh第一个Vnode比较(通过sameVnode(oldVnode, newVnode)), 后续逻辑见代码
- oldCh最后一个Vnode和newCh最后一个Vnode比较(通过sameVnode(oldVnode, newVnode)),后续逻辑见代码
- oldCh第一个Vnode和newCh最后一个Vnode比较,(通过sameVnode(oldVnode, newVnode)),后续逻辑见代码
- oldCh最后一个Vnode和newCh第一个Vnode比较,(通过sameVnode(oldVnode, newVnode)),后续逻辑见代码
- 执行到这里说明以上不适配,所以将oldCh的数组用一个对象来描述
newCh里面Vnode是否满足 Vnode.key in a,满足则可以使用patchVnode(),然后在移动真实dom(基本上都是使用originNode.insertBefore(newVnode, oldVnode)),不满足则说明不能复用旧dom,简单的全部新增newVnode,全部移除oldVnode。a = { //key: index weiyikey1: 1, weiyikey2: 2, weiyikey3: 3 }
流程图如下(通过chrome截取)
function patch (oldVnode, vnode, hydrating, removeOnly) {
...
if (!isRealElement && sameVnode(oldVnode, vnode)) {
// patch existing root node
patchVnode(oldVnode, vnode, insertedVnodeQueue, null, null, removeOnly);
}
'''
}
function patchVnode (
oldVnode,
vnode,
insertedVnodeQueue,
ownerArray,
index,
removeOnly
) {
...
if (isUndef(vnode.text)) {
if (isDef(oldCh) && isDef(ch)) {
if (oldCh !== ch) { updateChildren(elm, oldCh, ch, insertedVnodeQueue, removeOnly); }
} else if (isDef(ch)) {
{
checkDuplicateKeys(ch);
}
if (isDef(oldVnode.text)) { nodeOps.setTextContent(elm, ''); }
addVnodes(elm, null, ch, 0, ch.length - 1, insertedVnodeQueue);
} else if (isDef(oldCh)) {
removeVnodes(elm, oldCh, 0, oldCh.length - 1);
} else if (isDef(oldVnode.text)) {
nodeOps.setTextContent(elm, '');
}
} else if (oldVnode.text !== vnode.text) {
nodeOps.setTextContent(elm, vnode.text);
}
...
}
function updateChildren (parentElm, oldCh, newCh, insertedVnodeQueue, removeOnly) {
while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {
if (isUndef(oldStartVnode)) {
oldStartVnode = oldCh[++oldStartIdx]; // Vnode has been moved left
} else if (isUndef(oldEndVnode)) {
oldEndVnode = oldCh[--oldEndIdx];
} else if (sameVnode(oldStartVnode, newStartVnode)) {
patchVnode(oldStartVnode, newStartVnode, insertedVnodeQueue, newCh, newStartIdx);
oldStartVnode = oldCh[++oldStartIdx];
newStartVnode = newCh[++newStartIdx];
} else if (sameVnode(oldEndVnode, newEndVnode)) {
patchVnode(oldEndVnode, newEndVnode, insertedVnodeQueue, newCh, newEndIdx);
oldEndVnode = oldCh[--oldEndIdx];
newEndVnode = newCh[--newEndIdx];
} else if (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];
} else 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];
} else {
if (isUndef(oldKeyToIdx)) { oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx); }
idxInOld = isDef(newStartVnode.key)
? oldKeyToIdx[newStartVnode.key]
: findIdxInOld(newStartVnode, oldCh, oldStartIdx, oldEndIdx);
if (isUndef(idxInOld)) { // New element
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];
}
}
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(parentElm, oldCh, oldStartIdx, oldEndIdx);
}
}
}