Corn /vdom/patch.js (updateChildren method)

demo:

const app = new vue({ el:'#demo', data: {item: [' a ', 'b', 'c', 'd',]}, mounted () {setTimeout (() = > {this. Item. Splice (2, 0, 'E')})}})Copy the code

Insert a, B,e,c,d,e between B and C; But what happens when key is not applied?

Without a key, the program can’t figure out who to update. So you can only do one operation, that is to update everyone in sight, which will form a kind of error, the update that does not change is updated, the update that needs to be updated is not exactly updated.

So let’s look at the case where we have a key

// The first cycle is patch A A B C D A B E C D // the second cycle is patch B B C D B E C D // The third cycle is patch D C D E C D // the fourth cycle is patch C C E C // The last time there is only E left in the new array, the old array has all patch finished, create F, insert before CCopy the code

We now look at the source code interpretation

function updateChildren (parentElm, oldCh, newCh, insertedVnodeQueue, removeOnly) { 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, vnodeToMove, refElm // removeOnly is a special flag used only by <transition-group> // to ensure removed elements stay in correct relative positions // during leaving transitions const canMove = ! removeOnly if (process.env.NODE_ENV ! == 'production') { checkDuplicateKeys(newCh) } 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, PatchVnode (oldStartVnode, newStartVnode, insertedVnodeQueue, newCh)) {// Compare old start node with new start node newStartIdx) oldStartVnode = oldCh[++oldStartIdx] newStartVnode = newCh[++newStartIdx] } else if (sameVnode(oldEndVnode, PatchVnode (oldEndVnode, newEndVnode, insertedVnodeQueue, newCh)) {// Compare old end node with new end node newEndIdx) oldEndVnode = oldCh[--oldEndIdx] newEndVnode = newCh[--newEndIdx] } else if (sameVnode(oldStartVnode, NewEndVnode)) {// Compare the old start node with the new end node. newEndIdx) canMove && nodeOps.insertBefore(parentElm, oldStartVnode.elm, nodeOps.nextSibling(oldEndVnode.elm)) oldStartVnode = oldCh[++oldStartIdx] newEndVnode = newCh[--newEndIdx] } else if (sameVnode(oldEndVnode, newStartVnode)) {// Compare old end node with new start node 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(oldCh, oldStartIdx, oldEndIdx) } } function sameVnode (a, b) { return ( a.key === b.key && ( ( a.tag === b.tag && a.isComment === b.isComment && isDef(a.data) === isDef(b.data) && sameInputType(a, b) ) || ( isTrue(a.isAsyncPlaceholder) && a.asyncFactory === b.asyncFactory && isUndef(b.asyncFactory.error) ) ) )}Copy the code

Else if (sameVnode(oldStartVnode, newStartVnode)) {// The old start node is compared with the new start node

Then execute the sameVnode method to see the logic inside the sameVnode method.

First, determine whether the key of the old and new data is the same. Because both A. and B. key are undefined without key, the first judgment is true. And then we’ll see if the tag is the same, and of course the tag is the same. It’s not a comment, it’s not an input tag, it’s not an input tag, it’s the same node, it starts patching. An analogy, each step will take this layer, will be forced to update

Else if (sameVnode(oldEndVnode, newEndVnode)) {// Compare the old end node with the new end node, and so on. By the time you get to the end of the last loop, you’re left with E, and you’re in this judgment

if (oldStartIdx > oldEndIdx) {

// Increments the new index value by 1 to find which element to append before or after

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)

}

At this point oldStartIdx is 2, oldEndIdx is 1, so enter judgment. Execute the addVnodes method to append nodes

Conclusion: The function of key is mainly to update virtual DOM efficiently. From the source level, it performs the updateChildren method in daptch. Js to perform patch operation between nodes. If there is no key, the system considers that the same node is updated forcibly. If there is a key, the system updates the node based on whether the key is the same