react diff 算法
react 15
diff 数据结构
关键计算点
- Diff 算法性能突破的关键点在于“分层对比”;
- 类型一致的节点才有继续 Diff 的必要性;
- key 属性的设置,可以帮我们尽可能重用同一层级内的节点。
react 16
基于requestIdleCallback和优先级任务调度
1.requestIdleCallback
- window.requestIdleCallback(callback[, options])
浏览器提供的requestIdleCallback API中的Cooperative Scheduling可以让浏览器在空闲时间执行回调(开发者传入的方法),在回调参数中可以获取到当前帧(16ms)剩余的时间。利用这个信息可以合理的安排当前帧需要做的事情,如果时间足够,那继续做下一个任务,如果时间不够就歇一歇,调用requestIdleCallback来获知主线程不忙的时候,再继续做任务。
2.不同的任务分配不同的优先级,Fiber根据任务的优先级来动态调整任务调度,先做高优先级的任务
{
NoWork: 0, // No work is pending.
SynchronousPriority: 1, // 文本输入框
TaskPriority: 2, // 当前调度正执行的任务
AnimationPriority: 3, // 动画过渡
HighPriority: 4, // 用户交互反馈
LowPriority: 5, // 数据的更新
OffscreenPriority: 6, // 预估未来需要显示的任务
}
3.任务调度的过程是:
- 在任务队列中选出高优先级的fiber node执行,调用requestIdleCallback获取所剩时间,若执行时间超过了deathLine,或者突然插入更高优先级的任务,则执行中断,保存当前结果,修改tag标记一下,设置为pending状态,迅速收尾并再调用一个requestIdleCallback,等主线程释放出来再继续
- 恢复任务执行时,检查tag是被中断的任务,会接着继续做任务或者重做
- 一个任务单元执行结束或挂起,会调用基于requestIdleCallback的调度器,返回一个新的任务队列继续进行上述过程。
对生命周期的影响
- componentWillMount
- componentWillUpdate
- shouldComponentUpdate
- componentWillReceiveProps
为什么对reconciliation阶段进行拆分,commit阶段呢?
-
reconciliation阶段包含的主要工作是对current tree 和 new tree 做diff计算,找出变化部分。进行遍历、对比等是可以中断,歇一会儿接着再来。
-
commit阶段是对上一阶段获取到的变化部分应用到真实的DOM树中,是一系列的DOM操作。不仅要维护更复杂的DOM状态,而且中断后再继续,会对用户体验造成影响。在普遍的应用场景下,此阶段的耗时比diff计算等耗时相对短。
-
所以,Fiber选择在reconciliation阶段拆分