Researched the diff algorithm for a day, source code level [personal summary]

It took me a long time today to study the source code of the virtual dom and diff algorithm, summarize the information from various sources, and straighten out the entire logic myself, hoping to help everyone a little bit.

First, let’s talk about diff

In fact, it is also an inevitable product of virtual dom technology. It is not dedicated to vue. Other frameworks, as long as virtual dom is involved, have diff algorithms. Because if we only modify a small part of it when we write the page, but if it is directly rendered to the real dom, and then reflowed and redrawn, the overhead is very high, but the lovely diff! Can help us only modify the small piece we update . The key can help us efficiently update the virtual dom. The function of the key will be discussed later.

Two, diff in vue.

Mainly analyze from the three modules of the necessity , execution and efficiency of diff.

1. Necessity ---Why do we need diff algorithm?

First of all, we can see the method of the mountedcomponent of the source code lifecycle module.

When a component executes $mount once, a watcher will be created (the watcher corresponds to the component instance one by one). But there are likely to be multiple data in the component that use the key, but a vue2 component has only one watcher. In order to know who has changed during the update process more accurately, we need diff . (First, simply summarize diff as two comparisons of the old and new virtual doms, which can compare the changes, and give you an idea~)

2. Execution

Let's look at the source code, patchVnode, which is where the diff occurs. The patchVnode function is used to compare the children of two identical nodes.

Occurrence time: when the instance of the component executes the update function.

Overall strategy: Depth first, same layer comparison.

通俗的讲,先找孩子,所有孩子都比完了,才会开始去比较改节点的下一个同层级节点-找不同。

我在网上找了个图,希望可以帮助大家理解。请大家看,大家可以参考下执行顺序

整体逻辑:从源码中看,主要就是看双方是否都有孩子,若都有孩子,比孩子,也就是updatechildren函数(其实是一种递归);又分了一些情况,若一方有孩子,若都没有等等,请大家看源码:

之于以上,简单总结一下上面两张源码:

比较两个虚拟dom树,对根节点root进行执行patchvnode(比对oldVnode,newVnode)函数,比较两个根节点是否是相同节点。如果不同,直接替换(新增新的,删除旧的)。

如果相同,继续patchVnode,进一步比较属性,文本,子节点。进行更新,也就是增删改。只有当都存在子节点时,并且oldVnode === newVnode 为false时。(插一句:因为如果全等,代表子级肯定也完全相等,这还比较啥,不用比较了)会执行updateChildren函数,去进一步比较他们的子节点

3、高效性-----比孩子---updateChildren函数-----深度对比新旧节点的子节点

先假设头尾相同做四次比较(新旧的开始和结束来回重组),没找到就按照通用的方式遍历查找,查找结束,按情况在处理剩下的。。

(在头尾比较时)用key可以很精准的找到相同节点,从而找到准确的位置插入新节点。patch过程会很高效

如果不加key,永远认为是相同的节点,能做的操作只有强硬更新,避免不了频繁的更新过程,多很多dom操作,性能差。

总之,体现的就是一个循环递归:

patch —> patchVnode —> updateChildren —> patchVnode —> updateChildren —> patchVnode…

最后插一句关于key的:

key是vnode唯一的标识,且是不可变的。比如在我们码代码的时候,设置学号、id、身份证号这些唯一的。

key的作用是为了更高效的更新虚拟dom(提升渲染效率),也就是diff算法。比如我们想在bc中间加一个D,key可以做节点的唯一标识,告诉diff在更改前后是同一个节点。找到正确的位置插入新节点。

注意不要用index,因为比如中间插入数据,导致数据流标号的更改,index就变了,不仅会降低key的效率。严重的话会直接渲染出错。

Guess you like

Origin blog.csdn.net/m0_71981318/article/details/129039837