vue diff算法讲解

Source:https://zhuanlan.zhihu.com/p/81752104

因为Vue在视图层上的更新是通过Virtual Dom实现的(直接操作Dom开销很大)。Virtual DOM是一个对象,每次更新视图层时可以直接比较新旧Virtual DOM上各节点的差异,只对出现变化的节点进行更新。这样可以减少更新量,降低资源消耗。查找节点变化的算法便是Diff算法。

Diff 做法
Vue 只会对新旧节点中 父节点是相同节点 的 那一层子节点 进行比较。也可以说成是,只有两个新旧节点是相同节点的时候,才会去比较他们各自的子节点。
最大的根节点一开始可以直接比较。
这也叫做 同层级比较,并不需要递归,虽然好像降低了一些复用性,也是为了避免过度优化,是一种很高效的 Diff 算法

新旧节点是什么
所有的 新旧节点 指的都是 Vnode 节点,Vue 只会比较 Vnode 节点,而不是比较 DOM,因为 Vnode 是 JS 对象,不受平台限制,所以以它作为比较基础,代码逻辑后期不需要改动,拿到比较结果后,根据不同平台调用相应的方法进行处理就好了。
Vnode 更多信息:VNode - 源码版

父节点是相同节点是什么意思?
比如下图出现的 四次比较(从 first 到 fouth),他们的共同特点都是有 相同的父节点
比如 蓝色方的比较,新旧子节点的父节点是相同节点 1
比如 红色方的比较,新旧子节点的父节点都是 2
所以他们才有比较的机会

而下图中,只有两次比较,就是因为在 蓝色方 比较中,并没有相同节点,所以不会再进行下级子节点比较

Diff 比较逻辑
Diff 比较的内核是 节点复用,所以 Diff 比较就是为了在 新旧节点中 找到 相同的节点
这个的比较逻辑是建立在上一步说过的同层比较基础之上的,所以说,节点复用,找到相同节点并不是无限制递归查找。
比如下图中,的确 旧节点树 和 新节点树 中有相同节点 6,但是然并卵,旧节点6并不会被复用

就算在同一层级,然而父节点不一样,依旧然并卵

只有这种情况的节点会被复用,相同父节点 8

下面说说 Diff 的比较逻辑

1、能不移动,尽量不移动
2、没得办法,只好移动
3、实在不行,新建或删除

比较处理流程是下面这样

在新旧节点中

1、先找到 不需要移动的相同节点,消耗最小

2、再找相同但是需要移动的节点,消耗第二小

3、最后找不到,才会去新建删除节点,保底处理

比较是为了修改DOM树
其实这里存在三种树,一个是 页面DOM 树,一个是 旧VNode 树,一个是 新 Vnode 树

页面DOM树和旧VNode树 节点一一对应
而新Vnode树则是表示更新后 页面DOM树 该有的样子
这里把旧Vnode树和新Vnode树进行比较的过程中不会对这两棵Vode树进行修改,而是以比较的结果直接对 真实DOM 进行修改。
比如说,在旧Vnode树同一层中,找到和新Vnode树中一样但位置不一样节点,此时需要移动这个节点,但是不是移动旧Vnode树中的节点,而是直接移动DOM。
总的来说,新旧 Vnode 树是拿来比较的,页面DOM 树是拿来根据比较结果修改的。

An Example
比如下图存在这两棵 需要比较的新旧节点树 和 一棵 需要修改的页面 DOM树

第一轮比较开始:
因为父节点都是 1,所以开始比较他们的子节点
按照我们上面的比较逻辑,所以先找 相同 && 不需移动 的点
毫无疑问,找到 2


拿到比较结果,这里不用修改DOM,所以 DOM 保留在原地

第二轮比较开始
然后,没有 相同 && 不需移动 的节点了
只能第二个方案,开始找相同的点
找到 节点5,相同但是位置不同,所以需要移动


拿到比较结果,页面 DOM 树需要移动DOM 了,不修改,原样移动

第三轮比较开始
继续,相同节点也没得了,没得办法了,只能创建了
所以要根据 新Vnode 中没找到的节点去创建并且插入
然后旧Vnode 中有些节点不存在 新VNode 中,所以要删除

于是开始创建节点 6 和 9,并且删除节点 3 和 4

然后页面就完成更新啦

posted @ 2021-03-15 17:17  NullCream  阅读(307)  评论(0编辑  收藏  举报