计算两颗树形结构差别并进行转换vue
文中, 所说起n均表明节点的个数node
处理方案: 循环递归每个节点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³)优化
vue和react的虚拟DOM的diff算法大体相同,其核心是基于两个简单的假设:component
(优化的)diff三点策略:
即, 比较只会在同层级进行, 不会跨层级比较
基于以上优化的diff三点策略,react分别进行如下算法优化
react对树的算法进行了分层比较。react 经过 updateDepth对Virtual Dom树进行层级控制,只会对相同颜色框内的节点进行比较,即同一个父节点下的全部子节点。当发现节点不存在,则该节点和其子节点都会被删除。这样是须要遍历一次dom树,就完成了整个dom树的对比
分层比较 img
若是是跨层级的移动操做,如图
跨层级操做 img
当根结点发现A消失了,会删除掉A以及他的子节点。当发现D上多了一个A节点,会建立A(包括其子节点)节点做为子节点
因此:当进行跨层级的移动操做,react并非简单的进行移动,而是进行了删除和建立的操做,这会影响到react性能。因此要尽可能避免跨层级的操做。(例如:控制display来达到显示和隐藏,而不是真的添加和删除dom)
component vs img
若是组件D和组件G,若是类型不一样,可是结构相似。这种状况下,由于类型不一样,因此react会删除D,建立G。因此咱们可使用shouldComponentUpdate()返回false不进行diff。
针对react15, 16出了新的生命周期
因此:component diff 主要是使用shouldComponentUpdate() 来进行优化
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,减小没必要要的重渲染
vue2.0加入了virtual dom,和react拥有相同的 diff 优化原则
差别就在于, diff的过程就是调用patch函数,就像打补丁同样修改真实dom
updateChildren是vue diff的核心
过程能够归纳为:oldCh和newCh各有两个头尾的变量StartIdx和EndIdx,它们的2个变量相互比较,一共有4种比较方式。若是4种比较都没匹配,若是设置了key,就会用key进行比较,在比较的过程当中,变量会往中间靠,一旦StartIdx>EndIdx代表oldCh和newCh至少有一个已经遍历完了,就会结束比较