1. What is patch? Briefly what does Patch do?

As we have learned above, in order to save frequent DOM operation, we set up a vnode virtual node. According to the differences between the old and new nodes, we operate the real DOM. And this process of comparison, we call patch.

var patch = createPatchFunction();
Vue.prototype.__patch__ =  patch
Copy the code

What did Patch do?

  • Create new nodes (element nodes, comment nodes, text nodes)
  • Example Delete a node that does not exist
  • Modify the nodes to be updated

2. Patch process

3. Add a node

if (isUndef(oldVnode)) { // If oldValue is empty
      isInitialPatch = true; createElm(vnode, insertedVnodeQueue, parentElm, refElm); } For the distinction of three types of nodes: element node must have a tag, comment node isComment must betrueIf neither is satisfied, it is a text nodeif (isDef(tag)) {}
else if (isTrue(vnode.isComment)){}
else{}
Copy the code

4. Delete the node

function removeVnodes (vnodes, startIdx, endIdx) {
    for (; startIdx <= endIdx; ++startIdx) {
      var ch = vnodes[startIdx];
      if (isDef(ch)) {
        if (isDef(ch.tag)) {
          removeAndInvokeRemoveHook(ch); // Remove the DOM and execute the remove hook function
          invokeDestroyHook(ch); // Execute the Module deStory hook function and vNode deStory hook function
        } else { // Text noderemoveNode(ch.elm); }}}}Copy the code

5. Update the node

1.Static nodes will skip the update2.If the new node has a text property and is not equal to the old node's text, then call setTextContent directly to change the DOM node's content to the text saved by the text property, no matter what the previous child node was3.Determine if the new node has children, if no children. Then simply delete the childern of the old node. If there are children, we need to make a more detailed comparison of children, including deletion and addition. Moving position, etcCopy the code

6. The implementation strategy of DIFF is the updateChildren method

  • New front: the first of all unprocessed nodes in newChildren
  • New empress: The last node in newChildren that has not been processed
  • Old before: the first node of all unprocessed in oldChildren
  • Old empress: The last node in oldChildren that has not been processed

Compare update steps

Before new and before old

sameVnode(oldStartVnode, newStartVnode)

New tail and old tail

sameVnode(oldStartVnode, newStartVnode)

New tail and old head

sameVnode(oldStartVnode, newEndVnode)

If the nodes are the same then you need to move them. Source code is:

nodeOps.insertBefore(parentElm, oldStartVnode.elm, nodeOps.nextSibling(oldEndVnode.elm));
function insertBefore (parentNode, newNode, referenceNode) { parentNode.insertBefore(newNode, referenceNode); } the insertBefore function inserts at the end of the current parent if the referenceNode is nullCopy the code

New head and old tail

sameVnode(oldEndVnode, newStartVnode) / / to compare
nodeOps.insertBefore(parentElm, oldEndVnode.elm, oldStartVnode.elm) / / insert
Copy the code

Create an index to traverse the query

CreateKeyToOldIdx && findIdxInOld iterates to see if the new node can be found in the old node list. If not, create it directly. If it can be found, compare whether it is the same node, and move it to the front of the oldStartVnode node. If not, create it directly before oldStartVnodeCopy the code

Process the remaining nodes

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);
}
Copy the code

Attach the source code

  function updateChildren (parentElm, oldCh, newCh, insertedVnodeQueue, removeOnly) {
    var oldStartIdx = 0; 
    var newStartIdx = 0;
    var oldEndIdx = oldCh.length - 1;
    var oldStartVnode = oldCh[0];
    var oldEndVnode = oldCh[oldEndIdx];
    var newEndIdx = newCh.length - 1;
    var newStartVnode = newCh[0];
    var newEndVnode = newCh[newEndIdx];
    var oldKeyToIdx, idxInOld, vnodeToMove, refElm;

    while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {
      if (isUndef(oldStartVnode)) { // Determines if it exists because each time a node is matched, the node is set to undefined or false
        oldStartVnode = oldCh[++oldStartIdx]; // Vnode has been moved left
      } else if (isUndef(oldEndVnode)) {
        oldEndVnode = oldCh[--oldEndIdx];
      } else if (sameVnode(oldStartVnode, newStartVnode)) { // Old head, new head
        patchVnode(oldStartVnode, newStartVnode, insertedVnodeQueue);
        oldStartVnode = oldCh[++oldStartIdx];
        newStartVnode = newCh[++newStartIdx];
      } else if (sameVnode(oldEndVnode, newEndVnode)) { // Old tail and new tail
        patchVnode(oldEndVnode, newEndVnode, insertedVnodeQueue);
        oldEndVnode = oldCh[--oldEndIdx];
        newEndVnode = newCh[--newEndIdx];
      } else if (sameVnode(oldStartVnode, newEndVnode)) { // Old head and new tail
        patchVnode(oldStartVnode, newEndVnode, insertedVnodeQueue);
        canMove && nodeOps.insertBefore(parentElm, oldStartVnode.elm, nodeOps.nextSibling(oldEndVnode.elm));
        oldStartVnode = oldCh[++oldStartIdx];
        newEndVnode = newCh[--newEndIdx];
      } else if (sameVnode(oldEndVnode, newStartVnode)) { // Old tail and new head
        patchVnode(oldEndVnode, newStartVnode, insertedVnodeQueue);
        canMove && nodeOps.insertBefore(parentElm, oldEndVnode.elm, oldStartVnode.elm);
        oldEndVnode = oldCh[--oldEndIdx];
        newStartVnode = newCh[++newStartIdx];
      } else {
        if (isUndef(oldKeyToIdx)) {  // Create the serial number of the old node
        oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx); }
        idxInOld = isDef(newStartVnode.key)
          ? oldKeyToIdx[newStartVnode.key]
          : findIdxInOld(newStartVnode, oldCh, oldStartIdx, oldEndIdx);
        if (isUndef(idxInOld)) { // The new node cannot be found in the old node
          createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx);
        } else {
          vnodeToMove = oldCh[idxInOld];
          if (sameVnode(vnodeToMove, newStartVnode)) {
            patchVnode(vnodeToMove, newStartVnode, insertedVnodeQueue);
            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); }}Copy the code