Article directory
1 Introduction
In the introduction of the previous article VNode
, we said that VNode
the biggest use is to generate real DOM
corresponding virtual DOM
nodes before and after data changes, and then compare the old and new copies VNode
to find out the differences, and then update the nodes with differences DOM
, and finally achieve the following Minimal manipulation DOM
of the purpose of actually updating the view. And VNode
the process of comparing the old and new copies and finding the differences is the so-called DOM-Diff
process. DOM-Diff
Algorithms are DOM
the core of the entire virtual world, so next, we will start with the source code and study the process Vue
in depth DOM-Diff
.
2. patch
In Vue
, DOM-Diff
the process is called patch
the process. Patch, which means "patch", refers to repairing the old VNode
, patching to get a new one VNode
, very vivid. No matter what it is called, its essence is the process of comparing the old and new copies VNode
. When we study patch
the process below, we must grasp such an idea: the so-called old VNode
(that is oldVNode
) is the virtual node corresponding to the view before the data change DOM
, and the new one is the virtual node VNode
corresponding to the new view to be rendered after the data change , so we have to compare the old one with the new DOM
one generated , if there are nodes on the new one but not on the old one, then add them to the old one ; if there are no nodes on the new one but there are nodes on the old one , then delete it on the old one; if some nodes exist on both the new one and the old one , then the new one prevails and the old one is updated so that the old and new are the same.VNode
oldVNode
VNode
oldVNode
oldVNode
VNode
oldVNode
oldVNode
VNode
oldVNode
VNode
oldVNode
VNode
Maybe you feel a little confused, it doesn’t matter, we are talking about it in a more general way, you can understand it this way: suppose you have an old electronic document on your computer, and the boss gives you a new paper document at this time, and Tell you that most of the contents of these two documents are the same, let you take the new paper version as the standard, and make a new electronic version of the paper version and send it to the boss. For this task at this time, you should have two solutions: one solution is to delete all the content of the old document, and then type in the new paper document word by word, This kind of solution doesn't need to bother your brain, even if you are a little tired, you can solve the problem. The other solution is to use the new paper version as the benchmark to compare the old electronic version with the new paper version. If some parts are in the new document but not in the old document , then add these parts in the old document; if some parts are not in the new document but in the old document, then delete these parts in the old document; if some parts are in the new and old documents There are all in the document, then compare to see if there is anything that needs to be updated, and finally update it in the old document, and finally achieve the perfect solution of turning the old document into the same as the paper version in hand.
Comparing the above two options, it is obvious that you are as Vue
smart as and will definitely choose the second option. In the second solution, the old electronic version of the document corresponds to the one that has been rendered on the view oldVNode
, and the new paper version of the document corresponds to the new one that will be rendered on the view VNode
. In a word: Based on the new VNode, transform the old oldVNode to make it the same as the new VNode. This is what the patch process should do .
Having said so much, it sounds very complicated, but it is not. If we think about it carefully, the whole thing patch
is nothing more than three things:
- Create a node:
VNode
If there is one in the new one butoldVNode
not in the old one, it will be created in the old oneoldVNode
. - Delete node: If there
VNode
is no node in the new one but there is one inoldVNode
the old one, delete it from the old oneoldVNode
. - Update node: both new
VNode
and old , the new one prevails and the old one is updated .oldVNode
VNode
oldVNode
OK, at this point, you have half understood the process Vue
of middleware . Next, we will analyze one by one to see how to do the above three things.patch
Vue
3. Create a node
In the previous article, we analyzed that VNode
a class can describe 6 types of nodes, but in fact only 3 types of nodes can be created and inserted into it DOM
, they are: element nodes, text nodes, and comment nodes. Therefore, Vue
when creating a node, it will be judged which type of node the node is in the new one VNode
but not in the old one , and then call different methods to create and insert it into the node.oldVNode
DOM
In fact, it is not difficult to judge, because the characteristics of these three types of nodes are very obvious, how to judge in the source code:
// 源码位置: /src/core/vdom/patch.js
function createElm (vnode, parentElm, refElm) {
const data = vnode.data
const children = vnode.children
const tag = vnode.tag
if (isDef(tag)) {
vnode.elm = nodeOps.createElement(tag, vnode) // 创建元素节点
createChildren(vnode, children, insertedVnodeQueue) // 创建元素节点的子节点
insert(parentElm, vnode.elm, refElm) // 插入到DOM中
} else if (isTrue(vnode.isComment)) {
vnode.elm = nodeOps.createComment(vnode.text) // 创建注释节点
insert(parentElm, vnode.elm, refElm) // 插入到DOM中
} else {
vnode.elm = nodeOps.createTextNode(vnode.text) // 创建文本节点
insert(parentElm, vnode.elm, refElm) // 插入到DOM中
}
}
From the above code, we can see that:
- To judge whether it is an element node, you only need to judge
VNode
whether the node hastag
a label. If there istag
an attribute, it is regarded as an element node, and thecreateElement
method is called to create the element node. Usually, the element node will also have child nodes, then recursively traverse to create all child nodes, insert all child nodesinsert
into the current element node after creation, and finally put The current element node is inserted intoDOM
. - To judge whether it is a comment node, you only need to judge
VNode
whetherisComment
the attribute istrue
, if it istrue
, it is a comment node, then callcreateComment
the method to create a comment node, and then insert it intoDOM
. - If it is neither an element node nor a comment node, it is regarded as a text node, and the method is called
createTextNode
to create a text node, and then inserted into itDOM
.
In the code, all node operations
nodeOps
areVue
encapsulated for cross-platform compatibility. For example,nodeOps.createTextNode()
on the browser side, it is equivalent todocument.createTextNode()
The above completes the operation of creating a node, and its complete flow chart is as follows:
4. Delete node
If some nodes are not in the new one VNode
but in the old one oldVNode
, then these nodes need to be oldVNode
deleted from the old one. Removing a node is as simple as calling a method on the parent element of the node to be removed removeChild
. The source code is as follows:
function removeNode (el) {
const parent = nodeOps.parentNode(el) // 获取父节点
if (isDef(parent)) {
nodeOps.removeChild(parent, el) // 调用父节点的removeChild方法
}
}
5. Update the node
It is relatively simple to create and delete nodes, but it is relatively more complicated to update nodes. In fact, it is not too complicated, as long as the logic is cleared up, it can be understood.
Updating nodes is when some nodes are in both the new VNode
and the old oldVNode
, we need to compare them carefully to find out the difference and update them.
Before introducing the update node, let's introduce a small concept, that is, what is a static node? Let's look at an example:
<p>我是不会变化的文字</p>
The above node only contains plain text, without any variable variables. That is to say, no matter how the data changes, as long as the node is rendered for the first time, it will never change in the future. This is because It contains no variables, so any changes in the data are irrelevant to it. We call this kind of node a static node.
OK, with this concept in mind, we start updating nodes. When updating a node, we need to judge and deal with the following three situations:
-
If
VNode
both andoldVNode
are static nodesAs we said, no matter what changes in the data of static nodes, it has nothing to do with it, so if they are all static nodes, they can be skipped directly without processing.
-
If
VNode
it is a text nodeIf
VNode
it is a text node, it means that this node only contains plain text, then just checkoldVNode
whether it is also a text node, if so, then compare whether the two texts are different, and if they are different,oldVNode
change the text in it to be the same as the text in itVNode
. IfoldVNode
it is not a text node, no matter what it is, directly callsetTextNode
the method to change it into a text node, and the text content isVNode
the same as that of the text node. -
if
VNode
element nodeIf
VNode
it is an element node, the following two cases are subdivided:-
This node contains child nodes
If the new node contains child nodes, then it depends on whether the old node contains child nodes. If the old node also contains child nodes, it needs to recursively compare and update the child nodes; if the old node does not contain Child node, then the old node may be an empty node or a text node. If the old node is an empty node, create a copy of the child node in the new node and insert it into the old node. If the old node is a text node node, clear the text, then create a copy of the child nodes in the new node and insert it into the old node.
-
This node contains no child nodes
If the node does not contain child nodes and it is not a text node, it means that the node is an empty node, so it is easy to handle, no matter what was in the old node before, just empty it.
-
OK, after dealing with the above three situations, the update node is basically completed. Next, let’s see how it is implemented in the source code. The source code is as follows:
// 更新节点
function patchVnode (oldVnode, vnode, insertedVnodeQueue, removeOnly) {
// vnode与oldVnode是否完全一样?若是,退出程序
if (oldVnode === vnode) {
return
}
const elm = vnode.elm = oldVnode.elm
// vnode与oldVnode是否都是静态节点?若是,退出程序
if (isTrue(vnode.isStatic) &&
isTrue(oldVnode.isStatic) &&
vnode.key === oldVnode.key &&
(isTrue(vnode.isCloned) || isTrue(vnode.isOnce))
) {
return
}
const oldCh = oldVnode.children
const ch = vnode.children
// vnode有text属性?若没有:
if (isUndef(vnode.text)) {
// vnode的子节点与oldVnode的子节点是否都存在?
if (isDef(oldCh) && isDef(ch)) {
// 若都存在,判断子节点是否相同,不同则更新子节点
if (oldCh !== ch) updateChildren(elm, oldCh, ch, insertedVnodeQueue, removeOnly)
}
// 若只有vnode的子节点存在
else if (isDef(ch)) {
/**
* 判断oldVnode是否有文本?
* 若没有,则把vnode的子节点添加到真实DOM中
* 若有,则清空Dom中的文本,再把vnode的子节点添加到真实DOM中
*/
if (isDef(oldVnode.text)) nodeOps.setTextContent(elm, '')
addVnodes(elm, null, ch, 0, ch.length - 1, insertedVnodeQueue)
}
// 若只有oldnode的子节点存在
else if (isDef(oldCh)) {
// 清空DOM中的子节点
removeVnodes(elm, oldCh, 0, oldCh.length - 1)
}
// 若vnode和oldnode都没有子节点,但是oldnode中有文本
else if (isDef(oldVnode.text)) {
// 清空oldnode文本
nodeOps.setTextContent(elm, '')
}
// 上面两个判断一句话概括就是,如果vnode中既没有text,也没有子节点,那么对应的oldnode中有什么就清空什么
}
// 若有,vnode的text属性与oldVnode的text属性是否相同?
else if (oldVnode.text !== vnode.text) {
// 若不相同:则用vnode的text替换真实DOM的文本
nodeOps.setTextContent(elm, vnode.text)
}
}
The comments in the above code have been written very clearly. Next, we draw a flow chart to sort out the whole process. The flow chart is as follows:
By comparing the flowchart and code, I believe you can easily understand the logic of updating nodes.
In addition, you may have noticed that if VNode
both the old and the new contain child nodes, then the method for updating the child nodes is called in the code updateChildren
, and we will learn the logic of this method in the next article.
6. Summary
In this article we introduce the algorithm Vue
in DOM-Diff
: the patch process. We first introduced the entire thought process of the algorithm, and then by sorting out the algorithm idea, we learned that the whole patch
process does three things, namely: create nodes, delete nodes, and update nodes. And each thing has been carefully studied against the source code, and its logic flow chart has been drawn. In addition, for updating nodes, if both the new VNode
and old nodes contain child nodes, we need to update the child nodes in detail. We will learn about the process of updating child nodes in the next article.