β

浅谈React 16中的Fiber机制

有赞技术团队 43 阅读
浅谈React 16中的Fiber机制

九月份关于 React 开源协议的话题在社区闹得沸沸扬扬,所幸最后以 Facebook 宣布遵循 MIT 进行开源告终,但由此也足以看出 React 在前端圈的影响力。

作为 Facebook 前端出品的当门红,React 也迎来了一次大的升级,引入了最新的核心调度算法即 Fiber 机制,这是 React 基于 Fiber 调度的第一个版本,其次还增加了一些呼声高的新特性,如 render 方法返回数组、 debug 增加组件错误堆栈追踪等。本篇将主要介绍最新的Fiber调度机制。

React目前面临的挑战

React渲染页面分为两个阶段:

现有React一个非常大的问题是调度阶段是不可控的,什么意思?

假如我们更新一个 state,有1000个组件需要更新,每个组件更新需要1ms,那么我们就会有将近1s的时间,主线程被React占着用来调度,这段时间内用户的操作不会得到任何的反馈,只有当 React 中需要同步更新的任务完成后,主线程才被释放。对于这1s内 React 的调度,我们是无能为力的。

整个调度过程就如下图所示,组件树一旦过大,就会出现浏览器失去响应的情况,用户体验非常差。

浅谈React 16中的Fiber机制

Lin Clark 在 Fiber 分享视频 中对这一问题做了讲解,横向代表一次state变动需要做的任务,纵向代表在该时刻,主线程的占用情况。我们可以看到,随着 React 同步任务的执行,主线程将会一直被占用,无暇顾及其他任务。

Fiber解决方案

Fiber 的中文解释是 纤程 ,是线程的颗粒化的一个概念。也就是说一个线程可以包含多个 Fiber。

Fiber 的出现使大量的同步计算可以被 拆解 异步化 ,使浏览器主线程得以调控。从而使我们得到了以下权限:

把一个耗时长的任务分成很多小片,每一个小片的运行时间很短,虽然总时间依然很长,但是在每个小片执行完之后,都给其他任务一个执行的机会,这样唯一的线程就不会被独占,其他任务依然有运行的机会。

Fiber 把更新过程碎片化,执行过程如下面的图所示,每执行完一段更新过程,就把控制权交还给 React 负责任务协调的模块,看看有没有其他紧急任务要做,如果没有就继续去更新,如果有优先级更高的任务,那就去做优先级高的任务。
浅谈React 16中的Fiber机制

得益于同步任务的拆分,主线程可以短时间内被释放进行下一次的调用,不会出现页面失灵现象

Fiber的实现原理

React Fiber 的做法是不使用 Javascript 的栈,而是将需要执行的操作放在自己实现的栈对象上。这样就能在内存中保留栈帧,以便更加灵活的控制调度过程,例如我们可以手动操纵栈帧的调用。这对我们完成调度来说是至关重要。

大致上 Fiber 在调度的时候会执行如下流程:

  1. 将一个 state 更新需要执行的同步任务拆分成一个Fiber任务队列
  2. 在任务队列中选出优先级高的Fiber执行,如果执行时间超过了 deathLine ,则设置为 pending 状态挂起状态
  3. 一个Fiber执行结束或挂起,会调用基于 requestIdleCallback / requestAnimationFrame 实现的调度器,返回一个新的Fiber任务队列继续进行上述过程

浅谈React 16中的Fiber机制

requestIdleCallback 会让一个低优先级的任务在空闲期被调用,而 requestAnimationFrame 会让一个高优先级的任务在下一个栈帧被调用,从而保证了主线程按照优先级执行Fiber单元。

不同类型的任务会被分配不同的优先级,以下是关于优先级的定义:

module.exports = {  
  NoWork: 0, // No work is pending.
  SynchronousPriority: 1, // For controlled text inputs. 
  TaskPriority: 2, // Completes at the end of the current tick.
  AnimationPriority: 3, // Needs to complete before the next frame.
  HighPriority: 4, // Interaction that needs to complete pretty soon to feel responsive.
  LowPriority: 5, // Data fetching, or result from updating stores.
  OffscreenPriority: 6, // Won't be visible but do the work in case it becomes visible.
};

由此我们可以看出 Fiber 任务的优先级顺序为:

文本框输入 > 本次调度结束需完成的任务 > 动画过渡 > 交互反馈 > 数据更新 > 不会显示但以防将来会显示的任务

结语

由于Fiber的官方文档还未完成,所以笔者也只能从Fiber 开发者的博客,存档及会上的分享中寻找相关实现细节,但是毫无疑问这次React的升级绝对是干货满满。

作者:有赞技术团队
Thoughts, stories and ideas.
原文地址:浅谈React 16中的Fiber机制, 感谢原作者分享。

发表评论