面试系列:vue的diff算法的原理

259 阅读3分钟
diff 算法是一种通过同层的树节点进行比较的高效算法

其有两个特点:

1.比较只会在同层级进行, 不会跨层级比较

2.在diff比较的过程中,循环从两边向中间比较。

当数据发生改变时,订阅者watcher就会调用patch给真实的DOM打补丁。

它的patch过程是这样的:

1)首先对比当前同层的虚拟节点是否为同一种类型的标签,这是通过sameVnode(oldVnode,newVnode)方法来对比.具体比对的内容如下代码:

image.png

如果不是同一种类型的标签,那么就无需继续比对,直接把整个节点替换成新虚拟节点。

如果是同一种类型的标签,那么继续执行patchVnode方法进行深层比对;

2)patchVnode方法的过程是这样的:

1.首先找到对应的真实Dom,称为el;

2.然后判断newVnode和oleVnode是否指向同一个对象,如果是的话,那么直接return出来;

3.如果他们都有文本节点并且不相等,那么就直接将el的文本节点设置为newVode的文本节点;

4.如果oldVnode有子节点而newVnode没有,则删除el的子节点;

5.如果oldVnode没有子节点而newVnode有,则将newVnode的子节点真实化之后添加到el上;

6.如果两者都有子节点,则执行updateChildren函数比较子节点。

3)关于updateChildren方法是这样的:

这是一个新旧虚拟节点的子节点对比的方法,用的是首尾指针法,也就是新的子节点集合和旧的子节点集合都有首尾两个指针。

它的比较过程是循环从两边向中间比较。

让 旧虚拟节点的头尾指针 分别跟 新虚拟节点的头尾指针 进行比较,当 旧虚拟节点的指针 跟 新虚拟节点的指针 一样,那么就让 真实节点移动 到 新虚拟节点 的位置。

一个问题:为什么不建议用index作为key值?

因为假设这样一个场景,前提是把index作为key值,有三个li,都为文本节点,内容为a,b,c。现在我要在最前面加一个x的li,结果是全部的li都发生了更新,而如果key值是一个唯一的值比如id,那么只会更新一个li。

这是为什么呢?

因为数据发生变化,watcher要调用patch来给真实DOM打补丁,进行patch过程。

此时,利用sameVnode方法比对新旧首节点,这一步命中了逻辑,因为此时新旧首节点的key值都是0,同理1,2,也一样命中逻辑,会进行pacthVnode方法更新文本节点。而原本就有的c节点,此时key值为4,被当成了新结点。所以最终前三个进行了patchVnode更新文本节点,而最后一个是新增节点,所以结果就是四个li标签都更新了。

所以,使用index作为key值就会让vue在diff的时候可能复用错误的节点,多错了额外的工作。

引用来源:juejin.cn/post/699495…

以下是掘金别人文章的截图,作为参考: image.png

image.png

image.png

image.png

image.png