聊一聊Diff算法

场景

计算两颗树形结构差别并进行转换vue

文中, 所说起n均表明节点的个数node


传统Diff算法

处理方案: 循环递归每个节点react

传统diffweb

如上所示, 左侧树a节点依次进行以下对比:算法

a->e、a->d、a->b、a->c、a->adom

以后左侧树其它节点b、c、d、e亦是与右侧树每一个节点对比, 算法复杂度能达到O(n^2)函数

查找完差别后还需计算最小转换方式,这其中的原理我没仔细去看,最终达到的算法复杂度是O(n^3)性能

将两颗树中全部的节点一一对比须要O(n²)的复杂度,在对比过程当中发现旧节点在新的树中未找到,那么就须要把旧节点删除,删除一棵树的一个节点(找到一个合适的节点放到被删除的位置)的时间复杂度为O(n),同理添加新节点的复杂度也是O(n),合起来diff两个树的复杂度就是O(n³)优化


优化的Diff算法

vue和react的虚拟DOM的diff算法大体相同,其核心是基于两个简单的假设:component

  1. 两个相同的组件产生相似的DOM结构,不一样的组件产生不一样的DOM结构
  2. 同一层级的一组节点,他们能够经过惟一的id进行区分

(优化的)diff三点策略:

  • web UI中DOM节点跨层级的移动操做特别少,能够忽略不计。
  • 拥有相同类型的两个组件将会生成类似的树形结构,拥有不一样类型的两个组件将会生成不一样树形结构。
  • 对于同一层级的一组自节点,他们能够经过惟一id进行区分。

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

React优化Diff算法

基于以上优化的diff三点策略,react分别进行如下算法优化

  • tree diff
  • component diff
  • element diff
tree diff

react对树的算法进行了分层比较。react 经过 updateDepth对Virtual Dom树进行层级控制,只会对相同颜色框内的节点进行比较,即同一个父节点下的全部子节点。当发现节点不存在,则该节点和其子节点都会被删除。这样是须要遍历一次dom树,就完成了整个dom树的对比

分层比较 img

若是是跨层级的移动操做,如图

跨层级操做 img

当根结点发现A消失了,会删除掉A以及他的子节点。当发现D上多了一个A节点,会建立A(包括其子节点)节点做为子节点

因此:当进行跨层级的移动操做,react并非简单的进行移动,而是进行了删除和建立的操做,这会影响到react性能。因此要尽可能避免跨层级的操做。(例如:控制display来达到显示和隐藏,而不是真的添加和删除dom)

component diff
  • 若是是同类型的组件,则直接对比virtual Dom tree
  • 若是不是同类型的组件,会直接替换掉组件下的全部子组件
  • 若是类型相同,可是可能virtual DOM 没有变化,这种状况下咱们可使用shouldComponentUpdate() 来判断是否须要进行diff

component vs img

若是组件D和组件G,若是类型不一样,可是结构相似。这种状况下,由于类型不一样,因此react会删除D,建立G。因此咱们可使用shouldComponentUpdate()返回false不进行diff。

针对react15, 16出了新的生命周期

因此:component diff 主要是使用shouldComponentUpdate() 来进行优化

element diff

element diff 涉及三种操做:插入,移动,删除

不使用key的状况 img

不使用key的话,react对新老集合对比,发现新集合中B不等于老集合中的A,因而删除了A,建立了B,依此类推直到删除了老集合中的D,建立了C于新集合。=

酱紫会产生渲染性能瓶颈,因而react容许添加key进行区分

使用key的状况 img

react首先对新集合进行遍历,for( name in nextChildren),经过惟一key来判断老集合中是否存在相同的节点,若是没有的话建立,若是有的话,if (preChild === nextChild ) 进行移动操做

移动优化
在移动前,会将节点在新集合中的位置和在老集合中lastIndex进行比较,若是if (child._mountIndex < lastIndex) 进行移动操做,不然不进行移动操做。这是一种顺序移动优化。只有在新集合的位置 小于 在老集合中的位置  才进行移动。

若是遍历的过程当中,发如今新集合中没有,可是在老集合中的节点,会进行删除操做

因此:element diff 经过惟一key 进行diff 优化。

总结:
1.react中尽可能减小跨层级的操做。 2.可使用shouldComponentUpdate() 来避免react重复渲染。 3.添加惟一key,减小没必要要的重渲染

Vue优化Diff

vue2.0加入了virtual dom,和react拥有相同的 diff 优化原则

差别就在于, diff的过程就是调用patch函数,就像打补丁同样修改真实dom

  • patchVnode
  • updateChildren

updateChildren是vue diff的核心
过程能够归纳为:oldCh和newCh各有两个头尾的变量StartIdx和EndIdx,它们的2个变量相互比较,一共有4种比较方式。若是4种比较都没匹配,若是设置了key,就会用key进行比较,在比较的过程当中,变量会往中间靠,一旦StartIdx>EndIdx代表oldCh和newCh至少有一个已经遍历完了,就会结束比较

相关文章
相关标签/搜索