background
In vue, the diff algorithm of view update is often asked during the interview, so what is it?
Source code analysis
Here I'll first post the core code of diff algorithm
// isUndef determines whether it is undefined // oldCh old node list // newCh new node list // sameVnode determines whether it is the same node, key value, tag, data, etc while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) { if (isUndef(oldStartVnode)) { oldStartVnode = oldCh[++oldStartIdx] // Vnode has been moved left } else if (isUndef(oldEndVnode)) { oldEndVnode = oldCh[--oldEndIdx] } // From here, judge the old and new start and end nodes 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 { // Find out the logic to make a specific query for the current new node // Get the map of all key s between oldStartIdx and oldEndIdx if (isUndef(oldKeyToIdx)) oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx) // Judge whether the key of the new start node exists // If it exists, find this node in oldKeyToIdx // If it does not exist, it will find this node in the old node and reuse it 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] } } // Perform the final cleanup of the list 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) } }
It may not be intuitive from the code. Let's use pictures to show how some diff algorithms work.
example
Assume existing oldCh: [1,2,3,4,5] newCh[1,3,4]
The above is a simple example. There are still many scenes missing, such as two nodes, end node matching, start and end node matching, and so on.
But through the above example, you can also understand how diff works.
summary
So when asked about vue's diff algorithm, how should I answer it?
First, we get the array of old and new nodes, and then initialize four pointers to point to the start position and end position of the old and new nodes respectively. We compare them in pairs. If the new start node is the same as the old start node, they all move backward. If the end node matches, they all move forward. If the new start node matches the old end node, the old end node will be moved in front of the old start node. If the old start node matches the new end node, the old start node will be moved behind the old end node. If none of the above nodes are equipped with matching, a logical judgment will be made to judge whether the start node is in the old node. If it exists, it will be reused. If it does not exist, it will be created. Finally, jump out of the loop and cut or add. If the old start node is smaller than the old end node, the nodes between will be deleted. Otherwise, add a new start node to a new end node.
Before, I knew a little about vue's diff algorithm, but now I have a deeper understanding of diff algorithm.
ps: I was asked whether vue's diff algorithm is a depth first traversal algorithm or a breadth first algorithm. I can't know from the figure. updateChildren will be called in the patchVnode process, so vue's diff algorithm is a depth first algorithm.
The above may have some misunderstandings. Please correct and teach.