[Vue2.0 source code learning] Virtual DOM articles - DOM in Vue - update child nodes

1 Introduction

In the previous article, we learned about the process Vuein patch, DOM-Diffthe algorithm. And I know that patchthere are basically three things to do in the process, namely: create nodes, delete nodes and update nodes. It is relatively simple to create and delete nodes, and the logic of updating nodes is slightly more complicated because it has to deal with various possible situations, but it does not matter. We analyze the process, compare the source code, and draw a logic flow chart to help us understand the process. . Finally, we still have a problem left, that is, in the process of updating nodes, both old and new VNodemay contain child nodes, and there will be some additional logic for the comparison and update of child nodes, so in this article we will learn how Vueto How to compare and update child nodes.

2. Update child nodes

When both new VNodeand old are element nodes and both contain child nodes, then the attributes on the instances oldVNodeof these two nodes are the contained child node arrays. We record the new child node array as , and the old child node array as , and we compare the elements inside with the elements in the inside one by one. Comparing the two child node arrays must go through the loop, the outer layer Loop array, the inner loop array, every time a child node in the outer array is looped, go to the inner array to see if there is the same child node, the pseudo code is as follows:VNodechildrenVNodenewChildrenoldVNodeoldChildrennewChildrenoldChildrennewChildrenoldChildrennewChildrenoldChildren

for (let i = 0; i < newChildren.length; i++) {
    
    
  const newChild = newChildren[i];
  for (let j = 0; j < oldChildren.length; j++) {
    
    
    const oldChild = oldChildren[j];
    if (newChild === oldChild) {
    
    
      // ...
    }
  }
}

Then the above process will have the following four situations:

  • Create child nodes

    If newChildrena child node in it oldChildrencannot find the same child node in it, it means that newChildrenthe child node in it did not exist before and needs to be added this time, then create a child node.

  • delete child node

    If newChildrenafter looping through each child node inside, it is found that there oldChildrenare still unprocessed child nodes, which means that these unprocessed child nodes need to be discarded, and then delete these nodes.

  • move child node

    If newChildrena child node in it oldChildrenfinds the same child node in it, but the position is different, it means that this change needs to adjust the position of the child node, then use the position of the child node in the inside as a benchmark to adjust the position of the newChildrenchild node oldChildrenin the Position the node so that it newChildrenis the same as the position in .

  • update node

    If newChildrena child node in it oldChildrenfinds the same child node in it, and its position is also the same, then update oldChildrenthe node in it to make it newChildrenthe same as the node in it.

OK, at this point, the logic is relatively clear, and then we only need to deal with these four situations by category.

3. Create child nodes

If newChildrena child node in it oldChildrencannot find the same child node in it, it means that newChildrenthe child node in it did not exist before and needs to be added this time. Then we will create this node, and then create it. Insert it in DOMthe appropriate position.

Creating a node is very easy. We have already introduced it in Chapter 3 of the previous article, so I won't repeat it here.

So how to insert it into the appropriate position in the file after it is created DOM? DOMObviously, it is easy to insert nodes into , finding the right position is the key. Next, let's analyze how to find this suitable position. Let's look at the picture below:insert image description here

In the picture above, the new one is on the left VNodeand the old one is on the right oldVNode, which is also real DOM. This picture means that when we loop through newChildrenthe child nodes in the array, the first two child nodes oldChildrenfind the corresponding child nodes in it, then we process them, and mark them as processed after processing, when looping to the first two child nodes in the newChildrenarray When there are three child nodes, we find oldChildrenthat there is no corresponding child node in it, then we need to create this node. After creating it, we find that this node is the newChildrenthird child node from the left in the array, so we create it The node of is inserted into DOMthe third node position in the reality, that is, after all the processed nodes, OK, we applaud at this time, after all the processed nodes is the proper position we are looking for, but is this really the case? Let's look at the following picture again:insert image description here

If we insert the third node after all the processed nodes according to the above method, if the fourth node oldChildrencannot find a corresponding node in it, it is also a node that needs to be created, then when we insert the fourth node A node is also inserted into the processed node according to the above, and I found out how to insert it into the third position, but it is obvious that this node newChildrenis the fourth in the array!

This is the problem, in fact, we should insert the newly created node before all unprocessed nodes, so that the logic is correct. No matter how many new nodes are added later, each one is inserted before all unprocessed nodes, so that the position will not be wrong.

So, the proper position is before all unprocessed nodes, not after all processed nodes .

4. Delete child nodes

If you newChildrenloop through each child node inside, oldChildrenprocess it if you can find it in the array, and add it if you can’t find it, until you go through newChildrenall the child nodes inside and find that oldChildrenthere are still unprocessed child nodes , it means that these unprocessed child nodes need to be discarded, then delete these nodes.

It is also very easy to delete a node. We have already introduced it in Chapter 4 of the previous article, so I won't repeat it here.

5. Update child nodes

If newChildrena child node in it oldChildrenfinds the same child node in it, and its position is also the same, then update oldChildrenthe node in it to make it newChildrenthe same as the node in it.

Regarding the update node, we have already introduced it in Chapter 5 of the previous article, so I won't go into details here.

6. Move child nodes

If newChildrena child node in it oldChildrenfinds the same child node in it, but the position is different, it means that this change needs to adjust the position of the child node, then use the position of the child node in the inside as a benchmark to adjust the position of the newChildrenchild node oldChildrenin the Position the node so that it newChildrenis the same as the position in .

Similarly, it is not difficult to move a node, the key lies in where to move, or the key lies in where to move, and this position is the key. Let's look at the picture below:insert image description here

In the above figure, the two green nodes are the same node but in different positions, that is, the newChildrenthird child node inside is the same as the real DOMfourth oldChildrenchild node in it but in a different position. According to the above, we should Based newChildrenon the position of the inner child node, adjust the position of the inner node, so oldChildrenwe should move the fourth node inside to the position of the third node. It is not difficult to find through the annotation in the above figure that all unprocessed Before the node is the destination we want to move . If you say at this time, can it be moved to after all processed nodes? Then it goes back to the problem encountered when updating nodes: what if there are new nodes in front?DOMoldChildren

7. Go back to the source code

OK, the above are all the situations to be considered when updating child nodes. After the analysis, let’s go back to the source code to see if the actual situation is what we analyzed. The source code is as follows:

// 源码位置: /src/core/vdom/patch.js

if (isUndef(idxInOld)) {
    
        // 如果在oldChildren里找不到当前循环的newChildren里的子节点
    // 新增节点并插入到合适位置
    createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx)
} else {
    
    
    // 如果在oldChildren里找到了当前循环的newChildren里的子节点
    vnodeToMove = oldCh[idxInOld]
    // 如果两个节点相同
    if (sameVnode(vnodeToMove, newStartVnode)) {
    
    
        // 调用patchVnode更新节点
        patchVnode(vnodeToMove, newStartVnode, insertedVnodeQueue)
        oldCh[idxInOld] = undefined
        // canmove表示是否需要移动节点,如果为true表示需要移动,则移动节点,如果为false则不用移动
        canMove && nodeOps.insertBefore(parentElm, vnodeToMove.elm, oldStartVnode.elm)
    }
}

In the above code, first judge whether the child node of the current cycle can be oldChildrenfound in the inside , if not, add a new node and insert it into the appropriate position; if found, first compare whether the two nodes are the same, if they are the same, then newChildrenCall patchVnodethe update node first, and then check whether the node needs to be moved after the update is complete. Note that the source code uses shorthand for judging whether to move the child node. The following two writing methods are equivalent:

canMove && nodeOps.insertBefore(parentElm, vnodeToMove.elm, oldStartVnode.elm)
// 等同于
if(canMove){
    
    
  nodeOps.insertBefore(parentElm, vnodeToMove.elm, oldStartVnode.elm)
}

We see that the implementation in the source code is the same as what we analyzed.

8. Summary

In this article, we analyzed the outer loop array and the inner loop array Vuewhen updating child nodes , matching each element in the array with each element in the array, and creating and deleting child nodes according to different situations , Update child nodes, and move child nodes. And we have conducted in-depth analysis of different operations in different situations. After the analysis, we went back to the source code to verify the correctness of our analysis and found that our analysis was consistent with the implementation of the source code.newChildrenoldChildrennewChildrenoldChildren

Finally, let's think about another question: Although this double-layer loop can solve the problem, if the number of nodes is large, will the time complexity of the loop algorithm be very high? Is there any way to optimize it? The answer is of course yes, and Vuewe are aware of this and have optimized it. In the next article, we will analyze Vuehow to optimize the algorithm when the number of nodes is large.

Guess you like

Origin blog.csdn.net/weixin_46862327/article/details/130898722