Home > Article > Web Front-end > An in-depth analysis of the Diff algorithm in Vue2
The diff algorithm is an efficient algorithm that compares tree nodes at the same level, avoiding the need to search and traverse the tree layer by layer. So how much do you know about the diff algorithm? The following article will give you an in-depth analysis of the diff algorithm of vue2. I hope it will be helpful to you!
Because I often asked about the Diff algorithm when watching live interviews before, and the author himself uses Vue2 a lot, so I plan to study the Diff algorithm of Vue2. In fact, I have wanted to learn it for a long time. Yes, but maybe because I feel that the Diff algorithm is relatively profound, I have been putting off learning it. But recently I was preparing for an interview, so I thought I would learn it sooner or later. Why not take this opportunity to understand the Diff algorithm? The author spent a day on it. After some research, I may not fully understand the essence of the Diff algorithm. Please forgive me.
? This actually counts as the author's study notes, and the author's level is limited. The revised article only represents the author's personal opinion. If there are any errors, you can point them out in the comment area and they will be continuously improved. At the same time, this article is very long, so Readers, please read it patiently. After reading this, the author believes that you will have a deeper understanding of the Diff algorithm. I think this article is better than most of the articles on the Internet currently explaining the Diff algorithm, because this article starts from the problem and teaches everyone how to think, rather than starting directly from the answer, just like reading the answer, which feels meaningless. This article explains step by step Guide everyone to feel the subtlety of the Diff algorithm. At the same time, we also conducted a small experiment to give everyone a more intuitive experience of the Diff algorithm.
Because the bottom layer of Vue2 uses virtual DOM to represent the page Structural, virtual DOM is actually an object. If you want to know how to generate it, the general process is:
.vue
fileIn fact, the framework only uses virtual DOM for performance, because js generates DOM and then displays it Pages consume a lot of performance. If the entire page is regenerated every time it is updated, the experience will definitely not be good. Therefore, you need to find the changes in the two pages, and then just update the changed places with js(possibly Whether it is adding, deleting or updating) It only takes one click, which is the minimum amount of updates. So how to achieve the minimum amount of updates? Then we need to use the Diff algorithm. So what exactly does the Diff algorithm compare? Maybe this is where it is easy to misunderstand the Diff algorithm when you first learn it. In fact, it compares the old and new virtual DOM, so the Diff algorithm is to find the difference, find the difference between the two virtual DOMs, and then reflect the difference to On the page, this achieves the smallest amount of updates. If only the changed places are updated, the performance will definitely be better.
In fact, this is more difficult to explain. The author will give a rough introduction. Anyone who has learned Vue knows that one of the characteristics of this framework is data response. What is responsive style, that is, the page is also updated when the data is updated, so how does the page know that it needs to be updated? In fact, this is the clever part of this framework. The general process is as follows:
Object.defineProperty
's get
, then dependencies are collected here, then who collects dependencies? It is each component. Each component is a Watcher, which will record all the variables (that is, dependencies) in this component. Of course, each dependency (Dep) will also collect its own location. Watcher of the component;set
of Object.defineProperty
will be triggered. In this function, the data will notify everyone A Watcher updates the page, and then each page will call the update method. This method is patch
; In fact, we update while searching. In order to make it easier for everyone to understand, these two Parts are separated
When asked in an interview what the Diff algorithm is, everyone will definitely say a few words, such as 头头, tail tail, tail head, head Tail
, Depth-first traversal (dfs)
, Comparison of the same layer
Similar to these words, although I can say one or two sentences, it is just a taste.
In fact, the author read an article about the Diff algorithm published on CSDN, which is a highly read article. The author feels that he did not understand it clearly. Maybe he did not understand it himself, or he understood it but did not explain it clearly. Then the author will use his own Let me share my insights with you.
In order for everyone to understand clearly, here is the function calling process:
Diff algorithmPrerequisite
This is very important. You may ask what is the premise? Isn't it just the comparisons mentioned before? It's true but it's also wrong, because the Diff algorithm reaches the head, tail, tail, head, tail
mentioned before. The premise of this step is that the nodes compared twice are the same
, the similarity here is not exactly the same as everyone thinks, but it is the same if it meets certain conditions. To simplify the explanation, the article only considers one tag that only contains key
and tag name (tag)
, then what I said before is that the key is the same and the tag is the same
. In order to prove that the author's statement is somewhat correct, the source code is also posted here:
// https://github.com/vuejs/vue/blob/main/src/core/vdom/patch.ts // 36行 function sameVnode(a, b) { return ( a.key === b.key && a.asyncFactory === b.asyncFactory && ((a.tag === b.tag && a.isComment === b.isComment && isDef(a.data) === isDef(b.data) && sameInputType(a, b)) || (isTrue(a.isAsyncPlaceholder) && isUndef(b.asyncFactory.error))) ) }
If you are afraid of confusion, the following You can omit it and it won’t affect the overall understanding. The following is just a judgment added to consider all situations: So if the two virtual DOMs are not the same, there is no need to continue to compare, and if they are the same, there is no need to compare. The similarity here is really exactly the same, that is, the addresses of the two virtual DOMs are the same, so also paste the source code:
function patchVnode(......) { if (oldVnode === vnode) { return } ...... }
So far everyone may be confused, now let’s summarize:
patch
function is whether the old and new virtual DOM is key is the same and tag is the same
. If they are not the same, just replace them directly. If they are the same, use patchVnode
. Having said so much, the author actually wants to express a point. , that is, the Diff algorithm will be considered only when the two compared virtual DOMs are the same
. If they are not consistent, just delete the original one and replace it with the new DOM.
What is in this function is the real Diff algorithm, so I will introduce it to you based on the source code.
The core code in the source code is from line 638 to line 655 of patch.ts.
In fact, the current introduction of patchVnode is directly introduced to the source code, but you may not know why it is classified in this way, so the author is here to let you understand why it is classified in this way. First of all, Here is a conclusion:
text
attribute and the children
attribute cannot exist at the same time. This requires everyone to look at the template parsing source code partThen when comparing old and new nodes, the main comparison is whether there are text
and children
, then there will be the following nine situations
Situation | Old node text | Old node children | New node text | New node children | ||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | ❎ | ❎ | ❎ | ❎ | ||||||||||||||||||||||||||||||||
2 | ❎ | ✅ | ❎ | ❎ | ||||||||||||||||||||||||||||||||
✅ | ❎ | ❎ | ❎ | |||||||||||||||||||||||||||||||||
❎ | ❎ | ❎ | ✅ | |||||||||||||||||||||||||||||||||
❎ | ##✅❎ | ✅ | 6 | |||||||||||||||||||||||||||||||||
❎ | ❎ | ✅ | 7 | |||||||||||||||||||||||||||||||||
❎ | ✅ | ❎ | 8 | |||||||||||||||||||||||||||||||||
✅ | ✅ | ❎ | 9 | |||||||||||||||||||||||||||||||||
❎ | ✅ | ❎ |
实验 | 没有 key | key 为 index | key 唯一 |
---|---|---|---|
在队尾增加 | 1 | 1 | 1 |
在队中增加 | n - i + 1 | n - i + 1 | 1 |
在队首增加 | n + 1 | n + 1 | 1 |
表格为每次实验中,每种情况的最小更新量,假设列表原来的长度为 n
实验 | 没有 key | key 为 index | key 唯一 |
---|---|---|---|
在队尾删除 | 1 | 1 | 1 |
在队中删除 | n - i | n - i | 1 |
在队首删除 | n | n | 1 |
通过以上实验和表格可以得到加上 key 的性能和最小量更新的个数是最小的,虽然在 在队尾增加
和 在队尾删除
的最小更新量相同,但是之前也说了,如果没有 key 是要循环整个列表查找的,时间复杂度是 O(n),而加了 key 的查找时间复杂度为 O(1),因此总体来说加了 key 的性能要更好。
本文从源码和实验的角度介绍了 Diff 算法,相信大家对 Diff 算法有了更深的了解了,如果有问题可私信交流或者评论区交流,如果大家喜欢的话可以点赞 ➕ 收藏 ?
列举一些源码中出现的简单函数
setTextContent
function setTextContent(node: Node, text: string) { node.textContent = text }
isUndef
function isUndef(v: any): v is undefined | null { return v === undefined || v === null }
isDef
function isDef<T>(v: T): v is NonNullable<T> { return v !== undefined && v !== null }
insertBefore
function insertBefore( parentNode: Node, newNode: Node, referenceNode: Node ) { parentNode.insertBefore(newNode, referenceNode) }
nextSibling
function nextSibling(node: Node) { return node.nextSibling }
createKeyToOldIdx
function createKeyToOldIdx(children, beginIdx, endIdx) { let i, key const map = {} for (i = beginIdx; i <= endIdx; ++i) { key = children[i].key if (isDef(key)) map[key] = i } return map }
The above is the detailed content of An in-depth analysis of the Diff algorithm in Vue2. For more information, please follow other related articles on the PHP Chinese website!