做diff_手把手教你学会react-diff原理

44a33fdef4df979afad5621b544aec34.png

说明:本篇文章参考来源如下,我只是进行了总结归纳,删繁就简,精心整理后,自我感觉可读性更强,更容易理解和看到diff的全貌。

twobin:React 源码剖析系列 - 不可思议的 react diff​zhuanlan.zhihu.com
25c8df443b050f2e4284fc1ac633b628.png

1.传统diff算法

计算两颗树形结构差异的最优解一直是一个复杂且值得研究的问题,传统的diff算法,需要遍历整棵树的节点然后进行比较,是一个深度递归的过程,运算复杂度常常是O(n^3),这样的低效率在react中肯定是不能容忍的,那么react对于diff算法进行了哪些优化呢?

2.react diff的优化策略

  • DOM节点跨层级的操作不做优化,因为很少这么做,这是针对的tree层级的策略;
  • 对于同一个类的组件,会生成相似的树形结构,对于不同类的组件,生成不同的树形结构,这是针对conponent层级的策略;
  • 对于同一级的子节点,拥有同层唯一的key值,来做删除、插入、移动的操作,这是针对element层级的策略;

3.详解tree策略

比较策略:

1.只会对相同层级的节点进行比较; 2.只有删除、创建操作,没有移动操作; 3.由于没做性能优化,所以官方建议少做这样的跨层级操作

54cf32a7f8e457f4c3a369f4bac24d1d.png

场景分析:

如图所示,react发现新树中,R节点下没有了A,那么直接删除A,在D节点下创建A以及下属节点。过程就是删除、创建,直接粗暴。

4.详解component策略

比较策略:

1.如果是同一个类的组件,则会继续往下diff运算; 2.如果不是一个类的组件,那么直接删除旧的,创建新的;

tips: 对于同一个类的组件,用户可以控制其不要进行diff运算,具体就是,用户可以使用 shouldComponentUpdate()来告诉react要不要对此组件进行diff运算。

场景分析:

fa51bffe63e06e30e0cf5a36786f50ba.png

如图所示,当component D 换成了component G 后,即使两者的结构非常类似,但是react无视,骂骂咧咧的把component D删除后,重新创建了component G。

这也是合情合理的,因为基本不存在两个不同类但是组件结构相同的情况,如果有,只能证明使用者的代码需要优化,怎么会出现如此没有复用性的代码呢。

5.详解element策略

比较策略:

diff的时候,总是比较同一层级的节点们,这是前提,接下来提供了三种节点的操作:

  • 插入:INSERT_MARKUP
  • 删除:REMOVE_NODE
  • 移动:MOVE_EXISTING

注意:每个节点都有在他们的层级唯一的key作为标识。

场景分析:

_mountIndex:标识节点在老集合中的位置; lastIndex:在diff过程中访问的index的最大值,一会会用到。

52ceacc4cebd04c89a91451d0b61cf32.png

对比开始,有一个优化的原则:如果当前节点在新集合中的位置比老集合中的位置靠前的话,是不会影响后续节点操作的,所以,这种情况就不用管,这个被对比的节点不会动。

具体到运算上,就是只要lastIndex < _mountIndex,就不会动这个节点了。

这个lastIndex可不是新集合中的位置,而是访问过的位置的最大值,每对比一轮就会变化的,只是个数学参照。知道了这个点,就很好推算了,下面开始:

  • 节点B:此时lastIndex=0,_mountIndex=1;满足lastIndex < _mountIndex,因此B节点不动,此时lastIndex = Math.max(_mountIndex, lastIndex),就是1;
  • 节点A:此时lastIndex=1,_mountIndex=0;不满足lastIndex < _mountIndex,因此A节点进行移动操作,此时lastIndex = Math.max(_mountIndex, lastIndex),还是1;
  • 节点D:此时lastIndex=1,_mountIndex=3;满足lastIndex < _mountIndex,因此D节点不动,此时lastIndex = Math.max(_mountIndex, lastIndex),就是3;
  • 节点C:此时lastIndex=3,_mountIndex=2;不满足lastIndex < _mountIndex,因此C节点进行移动操作,当前已经比较完了

尽管ABCD节点都已经比较完了,但是diff过程还没完,还会整体遍历老集合中节点,看有没有没用到的节点,有的话,就删除。

待优化的部分:

14cf95326f47ae7d30489a121008e19c.png

如图所示,新集合中明明只有D节点进行了前置操作,别的节点顺序都没动,但是按照react的element diff的算法,仍然需要对所有的四个节点进行遍历计算,这是没有必要的,但是react也没有好的处理方式,所以官方建议:

尽量不要将一个节点从末尾提到最前边的类似的做法,会有性能损失。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值