Article directory
1 Introduction
In the previous article, we learned about the process Vue
in patch
, DOM-Diff
the algorithm. And I know that patch
there 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 VNode
may 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 Vue
to How to compare and update child nodes.
2. Update child nodes
When both new VNode
and old are element nodes and both contain child nodes, then the attributes on the instances oldVNode
of 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:VNode
children
VNode
newChildren
oldVNode
oldChildren
newChildren
oldChildren
newChildren
oldChildren
newChildren
oldChildren
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
newChildren
a child node in itoldChildren
cannot find the same child node in it, it means thatnewChildren
the child node in it did not exist before and needs to be added this time, then create a child node. -
delete child node
If
newChildren
after looping through each child node inside, it is found that thereoldChildren
are still unprocessed child nodes, which means that these unprocessed child nodes need to be discarded, and then delete these nodes. -
move child node
If
newChildren
a child node in itoldChildren
finds 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 thenewChildren
child nodeoldChildren
in the Position the node so that itnewChildren
is the same as the position in . -
update node
If
newChildren
a child node in itoldChildren
finds the same child node in it, and its position is also the same, then updateoldChildren
the node in it to make itnewChildren
the 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 newChildren
a child node in it oldChildren
cannot find the same child node in it, it means that newChildren
the 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 DOM
the 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
? DOM
Obviously, 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:
In the picture above, the new one is on the left VNode
and the old one is on the right oldVNode
, which is also real DOM
. This picture means that when we loop through newChildren
the child nodes in the array, the first two child nodes oldChildren
find 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 newChildren
array When there are three child nodes, we find oldChildren
that 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 newChildren
third child node from the left in the array, so we create it The node of is inserted into DOM
the 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:
If we insert the third node after all the processed nodes according to the above method, if the fourth node oldChildren
cannot 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 newChildren
is 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 newChildren
loop through each child node inside, oldChildren
process it if you can find it in the array, and add it if you can’t find it, until you go through newChildren
all the child nodes inside and find that oldChildren
there 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 newChildren
a child node in it oldChildren
finds the same child node in it, and its position is also the same, then update oldChildren
the node in it to make it newChildren
the 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 newChildren
a child node in it oldChildren
finds 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 newChildren
child node oldChildren
in the Position the node so that it newChildren
is 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:
In the above figure, the two green nodes are the same node but in different positions, that is, the newChildren
third child node inside is the same as the real DOM
fourth oldChildren
child node in it but in a different position. According to the above, we should Based newChildren
on the position of the inner child node, adjust the position of the inner node, so oldChildren
we 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?DOM
oldChildren
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 oldChildren
found 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 newChildren
Call patchVnode
the 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 Vue
when 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.newChildren
oldChildren
newChildren
oldChildren
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 Vue
we are aware of this and have optimized it. In the next article, we will analyze Vue
how to optimize the algorithm when the number of nodes is large.