patchVnode(oldEndVnode, newStartVnode, insertedVnodeQueue)
// 插入到老的开始节点的前面
canMove && nodeOps.insertBefore(parentElm, oldEndVnode.elm, oldStartVnode.elm)
oldEndVnode = oldCh[--oldEndIdx] newStartVnode = newCh[++newStartIdx] } else {
// 如果以上条件都不满足,那么这个时候开始比较key值,首先建立key和index索引的对应关系
if (isUndef(oldKeyToIdx)) oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx)
idxInOld = isDef(newStartVnode.key) ? oldKeyToIdx[newStartVnode.key] : null
// 如果idxInOld不存在
// 1. newStartVnode上存在这个key,但是oldKeyToIdx中不存在
// 2. newStartVnode上并没有设置key属性
if (isUndef(idxInOld)) { // New element
// 创建新的dom节点
// 插入到oldStartVnode.elm前面
// 参见createElm方法
createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm)
newStartVnode = newCh[++newStartIdx] } else {
elmToMove = oldCh[idxInOld] /* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && !elmToMove) {
warn(
'It seems there are duplicate keys that is causing an update error. ' +
'Make sure each v-for item has a unique key.'
)
// 将找到的key一致的oldVnode再和newStartVnode进行diff
if (sameVnode(elmToMove, newStartVnode)) {
patchVnode(elmToMove, newStartVnode, insertedVnodeQueue)
oldCh[idxInOld] = undefined
// 移动node节点
canMove && nodeOps.insertBefore(parentElm, newStartVnode.elm, oldStartVnode.elm)
newStartVnode = newCh[++newStartIdx] } else {
// same key but different element. treat as new element
// 创建新的dom节点
createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm)
newStartVnode = newCh[++newStartIdx] }
}
}
}
// 如果最后遍历的oldStartIdx大于oldEndIdx的话
if (oldStartIdx > oldEndIdx) { // 如果是老的vdom先被遍历完
refElm = isUndef(newCh[newEndIdx + 1]) ? null : newCh[newEndIdx + 1].elm
// 添加newVnode中剩余的节点到parentElm中
addVnodes(parentElm, refElm, newCh, newStartIdx, newEndIdx, insertedVnodeQueue)
} else if (newStartIdx > newEndIdx) { // 如果是新的vdom先被遍历完,则删除oldVnode里面所有的节点
// 删除剩余的节点
removeVnodes(parentElm, oldCh, oldStartIdx, oldEndIdx)
}
}
在开始遍历diff前,首先给oldCh和newCh分别分配一个startIndex和endIndex来作为遍历的索引,当oldCh或者newCh遍历完后(遍历完的条件就是oldCh或者newCh的startIndex >= endIndex),就停止oldCh和newCh的diff过程。接下来通过实例来看下整个diff的过程(节点属性中不带key的情况):










