-
Notifications
You must be signed in to change notification settings - Fork 0
Description
首先了解一些理念
diff算法
react使用 virtual dom 来表示 dom 树,而 diff 算法就是用于比较 virtual dom 树的区别,并更新界面需要更新的部分。diff 算法和 virtual dom 的完美结合的过程被称为 reconciler,有了 reconciler,开发者可以脱身操作真实的 dom 树

在 react16 之前的 reconciler 叫 stack reconciler,fiber 是 react 新的 reconciler,这次更新到 fiber 架构是一次重量级的核心架构的替换
Fiber相关
- 为何出现?
diff的时候时间太长,会阻塞交互
- 怎么处理
引入了异步渲染的概念,采用fiber架构
- 和原来的对比
原先的 stack reconciler 像是一个递归执行的函数,从父组件调用子组件的 reconciler 过程就是一个递归执行的过程,这也是为什么被称为 stack reconciler 的原因。当我们调用 setState 的时候,react 从根节点开始遍历,找出所有的不同,而对于特别庞大的__ dom 树来说,这个递归遍历的过程会消耗特别长的时间。这个过程任何交互都会阻塞。
- 具体优化的方案
fiber 的出现解决了这个问题,它把 reconciler 的过程拆分成了一个个的小任务,并在完成了小任务之后暂停执行 js 代码,然后检查是否有需要更新的内容和需要响应的事件,做出相应的处理后再继续执行 js 代码。
Fiber如何做到异步
- 前提
在做显示方面的工作时,经常会听到一个目标叫 60 帧,这表示的是画面的更新频率,也就是画面每秒钟更新 60 次。这是因为在 60 帧的更新频率下,页面在人眼中显得流畅,无明显卡顿。每秒钟更新 60 次也就是每 16ms 需要更新一次页面,如果更新页面消耗的时间不到 16ms,那么在下一次更新时机来到之前会剩下一点时间执行其他的任务,只要保证及时在 16ms 的间隔下更新界面就完全不会影响到页面的流畅程度。fiber 的核心正是利用了 60 帧原则,实现了一个基于优先级和 requestIdleCallback 的循环任务调度算法。
- 关于requestIdleCallback
假如某一帧里面要执行的任务不多,在不到16ms(1000/60)的时间内就完成了上述任务的话,那么这一帧就会有一定的空闲时间,这段时间就恰好可以用来执行requestIdleCallback的回调,由于requestIdleCallback利用的是帧的空闲时间,所以就有可能出现浏览器一直处于繁忙状态,导致回调一直无法执行,那么这种情况我们就需要在调用requestIdleCallback的时候传入第二个配置参数timeout了
requestIdleCallback(myNonEssentialWork, { timeout: 2000 });
function myNonEssentialWork (deadline) {
// 当回调函数是由于超时才得以执行的话,deadline.didTimeout为true
while ((deadline.timeRemaining() > 0 || deadline.didTimeout) &&
tasks.length > 0) {
doWorkIfNeeded();
}
if (tasks.length > 0) {
requestIdleCallback(myNonEssentialWork);
}
}- fiber的具体实现
fiber 利用了这个参数,判断当前剩下的时间是否足够继续执行任务,如果足够则继续执行,否则暂停任务,并调用 requestIdleCallback 通知浏览器空闲的时候继续执行当前的任务。
function fiber(剩余时间) {
if (剩余时间 > 任务所需时间) {
做任务;
} else {
requestIdleCallback(fiber);
}
}fiber 还会为不同的任务设置不同的优先级,高优先级任务是需要马上展示到页面上的,
{
Synchronous: 1, // 同步任务,优先级最高
Task: 2, // 当前调度正执行的任务
Animation 3, // 动画
High: 4, // 高优先级
Low: 5, // 低优先级
Offscreen: 6, // 当前屏幕外的更新,优先级最低
}- fiber架构
在 fiber 架构中,有一种数据结构,它的名字就叫做 fiber,这也是为什么新的 reconciler 叫做 fiber 的原因。fiber 其实就是一个 js 对象,这个对象的属性中比较重要的有 stateNode、tag、return、child、sibling 和 alternate。
Fiber = {
tag // 标记任务的进度
return // 父节点
child // 子节点
sibling // 兄弟节点
alternate // 变化记录
.....
};我们可以看出 fiber 基于链表结构,拥有一个个指针,指向它的父节点子节点和兄弟节点,在 diff 的过程中,依照节点连接的关系进行遍历。
目前可能的问题
在 fiber 中,更新是分阶段的,具体分为两个阶段,首先是 reconciliation 的阶段,这个阶段在计算前后 dom 树的差异,然后是 commit 的阶段,这个阶段将把更新渲染到页面上。第一个阶段是可以打断的,因为这个阶段耗时可能会很长,因此需要暂停下来去执行其他更高优先级的任务,第二个阶段则不会被打断,会一口气把更新渲染到页面上。
由于 reconciliation 的阶段会被打断,可能会导致 commit 前的这些生命周期函数多次执行。react 官方目前已经把 componentWillMount、componentWillReceiveProps 和 componetWillUpdate 标记为 unsafe,并使用新的生命周期函数 getDerivedStateFromProps 和 getSnapshotBeforeUpdate 进行替换。

