为什么用diff优化算法
因为用浏览器中实际操作DOM代价是很“价格昂贵”的,所以才会在Vue引进了 Virtual DOM,即便采用了Virtual DOM去进行真正DOM的3D渲染,在页面更新时,也无法全量地把整粒Virtual DOM开展3D渲染,而是先3D渲染变化的一部分,这个时候就需要一个测算Virtual DOM树更改一部分的算法了。
传统式diff优化算法
传统Diff优化算法根据循环系统递归算法对连接点进行对比,随后分辨每个节点状态及其需要做的实际操作(add,remove,change),最终 依据Virtual DOM开展DOM的3D渲染
例如左边树每个节点与右边树每个节点分别进行赋值比照(A->A、A->D、A->B、A->C ......),算法复杂度O(n^2),在每一个周期中,还要较为2个原素是不是相同,这就需要进行一次附加能量循环,算法复杂度为O(n)。因而,总算法复杂度为O(n^2 * n) = O(n^3)。
vue2 diff优化算法
vue数据推动
- 根据Object.defineProperty函数公式改写了数据库的getter和setter函数公式,来达到依靠收集和发放升级
- 一个key值对应一个Dep案例,一个Dep案例能够包括好几个Watcher,一个Wathcer还可以包括好几个Dep
- Dep用以依靠的搜集及管理,并告知相对应的Watcher实行对应的实际操作
- 依靠收集到的机会要在实行render方式时,载入vm上的数据,开启getter函数公式。而发放升级则在变动数据库的情况下,触发setter函数公式,根据dep.notify(),通知到所收集到的watcher,实行相对应实际操作
diff算法原理图
diff算法对策
深度优先,同层较为
- 较为只能在同一个层开展,不容易跨层较为
- 较为的过程当中,循环系统从两侧向中心融入
updateChildren
- 平级较为,降低比照频次,提升比照特性,算法复杂度在O(n)
- 头尾表针法
- 先后核对,当完成后撤出现阶段较为
- 3D渲染构造以newVnode为标准
- 每一次较为完成后,start点和end点向中心融入
- 当新老连接点中有一个start点跑进end点右边时,停止较为
- 如果配对不上,则旧虚似dom key只去比照新虚似dom的ke值,假如key同样则服食,并移到新虚似dom位置
核对次序
- 最先,旧虚拟节点的start跟新虚拟节点的start做核对,要是没有核对取得成功,就用旧虚拟节点的start跟新虚拟节点的end做核对
- 假如依然没有核对取得成功,就用旧虚拟节点的end跟新虚拟节点的start做核对,假如依然没有取得成功,就虚拟节点的end跟新虚拟节点的end做核对
- 当核对取得成功,就撤出核对,3D渲染结论都会以新虚拟节点得到的结果为标准
- 每一次核对完成后,核对成功start会偏移,end会往左边挪动。在运动的过程当中,当start点跑进end的右边就停止较为
vue3的diff优化算法
静态数据连接点
Vue3的编译程序会到编译程序环节对模具开展静态分析,将静态数据连接点和发展趋势连接点分离,将静态数据连接点标记为变量定义,并把它生成为一个独立的变量定义连接点。在3D渲染时,Vue3会直接用变量定义连接点,而无需再次创建节点,进而提升3D渲染特性。能够手动式标识静态数据连接点:
<template v-slot:header>
<h1>静态数据头顶部</h1>
</teamplate>
vue3 diff优化算法是一种按顺序较为
,在同一个层连接点较为的前提下,将较为的范畴延伸至新老DOM树中,适用更复杂节点构造。充分理由虚似dom中节点构造信息内容。如静态数据节点提升,会把静态数据连接点
分割出去独立解决。
当 Vue3 Patch 函数公式接受到新老 VNode 后,会使新老 VNode 树开展上述 Profiling 全过程,再将标识过节点传送来。Patch 函数公式在获得标识后二棵树后,最先递归算法较为新老树的根连接点是不是同样。
假如不同样,那样同 React Diff 优化算法一样,搜索新 VNode 在旧 VNode 二维数组中存不存在。假如旧 VNode 当中存有,意味着这是一个新增加连接点,直接用 createVNode 建立 newNode 并插入旧 VNode 的 DOM 中;不然的话,有可能是新 VNode 与某一旧 VNode 有同个父节点中的兄弟节点,也有可能是新 VNode 嵌入在某一个旧 VNode 的子树中。
针对两种情况依次进行解决:
状况1:新老 VNode 是兄弟节点
在这样的情况下,Vue3 Diff 优化算法会使用“按顺序较为”的思路。会形成新老 VNode 的 keys 二维数组,再对2个 keys 二维数组做一次 fuzzy 的配对,得到一个新老 keys 二维数组的映射表(Map)。依据映射表的信息就可以知道新老 VNode 的映射关系,进而了解该干什么去完成升级。
举例说明:
<!-- before -->
<div>
<!-- 旧 VNode -->
<p key="a">A</p>
<p key="b">B</p>
<p key="c">C</p>
</div>
<!-- after -->
<div>
<!-- 新 VNode -->
<p key="c">C</p>
<p key="b">B</p>
<p key="d">D</p>
</div>
<!-- 最先形成新老 VNode 的 keys 二维数组: -->
<!-- Old: ["a", "b", "c"] -->
<!-- New: ["c", "b", "d"] -->
<!-- 根据 fuzzy 配对获得以内的映射表: -->
<!-- M: {a: undefined, b: 1, c: 0, d: undefined} -->
<!-- 这时将 VNode 投射为 [C, B, D],而旧 VNode 投射为 [A, B, C]。 -->
<!-- 接下来工作中,就是按照映射关系,在旧连接点内进行增删改实际操作。 -->
<!-- 最后完成 DOM 如下所示: -->
<div>
<p key="c">C</p>
<!-- 老旧连接点 b 成了一个新的 b -->
<p key="b">B</p>
<!-- 增加了一个连接点 d -->
<p key="d">D</p>
</div>
状况2:新 VNode 在旧 VNode 里的子节点里
假如新 VNode 在旧 VNode 子树正中间,那就需要把它递归算法升级。
在赋值新老 VNode 期内,Vue3 Diff 优化算法会依据 key 值或 index 值优先选择配对新老 VNode,这可以显著提升特性。与此同时,出自于改善的目地,Vue3 Diff 优化算法还对一些特殊状况进行了特别处理,比如解决静态数据连接点,解决二维数组后的提升,等。
总的来说,Vue3 Diff 优化算法使用了更加有效且灵活多变的“按顺序较为”对策,提升了数据处理方法速度和高效率,以此来实现更快地页面渲染并能够更好地回复客户的实际操作。
vue和react的diff优化算法较为
- vue和react的diff优化算法,全是忽视越级较为,就做平级较为。vue diff时激发patch函数公式,主要参数是vnode和oldVnode,分别对应新老连接点。
- vue比照连接点。当连接点原素同样,可是classname不一样,称之为不同种类的原素,删掉复建,而react称之为同类产品连接点,仅仅改动连接点特性。
- vue的目录比照,使用的是两边到正中间核对的形式,而react使用的是从左往右先后参考的方法。当一个结合只是将最后一个连接点挪到了第一个,react会将前边节点先后挪动,而vue只会将最后一个连接点挪到第一个
汇总
- react-diff: 二端比照(目录次序转变会损害特性)
- vue-diff: 双指针方法比照(外逃太深节点结论也会导致优化算法功能失效)
- 总体来说vue的diff优化算法更好,整体规律性便是寻找新节点对应的旧连接点页面上节点,然后给真实相对应的dom移到恰当位置
参照
- vue里的diff优化算法
- vue优化算法详细说明