浅谈React Fiber

一、出现的缘由

在页面元素很多,且需要频繁刷新的场景下,React 15 会出现掉帧的现象。请看以下例子:

其根本原因,是大量的同步计算任务阻塞了浏览器的 UI 渲染。

默认情况下,JS 运算、页面布局和页面绘制都是运行在浏览器的主线程当中,他们之间是互斥的关系。如果 JS 运算持续占用主线程,页面就没法得到及时的更新。

当我们调用setState更新页面的时候,React 会遍历应用的所有节点,计算出差异,然后再更新 UI。整个过程是一气呵成,不能被打断的。如果页面元素很多,整个过程占用的时机就可能超过 16 毫秒,就容易出现掉帧的现象。

针对这一问题,React 团队从框架层面对 web 页面的运行机制做了优化,得到很好的效果。

所以,React Fiber简单来说就是一个从React v16开始引入的新协调引擎,用来实现Virtual DOM的增量渲染。

说人话:就是一种比线程还要细粒度的处理机制、一种能让React视图更新过程变得更加流畅顺滑的处理手法。

二、底层实现

出现掉帧的原因是因为 React 的前任 Reconciler —— Stack Reconciler,这种通过栈的方式实现任务调度的方式导致的。

Stack 这种架构的特点就是,所有任务都按顺序的压入了栈中,而执行的时候无法确认当前的任务是否会耗去过长的脚本运行时间,使得这 16ms 里浏览器能做的事不可控,甚至让 fetch data 这类实时意义很大的任务要等很久才能执行。

而对于fiber来说:

把耗时长的更新任务拆解成一个个小的任务分片,每执行完一个小的任务分片,都归还一次主线程,看看有没有什么其他紧急任务要做。如果在归还主线程时恰巧发现有紧急任务,那么会马上停掉当前更新任务,转而让主线程去做紧急任务,等主线程做完紧急任务,再重新做更新任务。(注意⚠️:是重新!不是从上次被打断的点继续);如果没有紧急任务,才敢唯唯诺诺地继续做接下来的任务分片。

如此一来,我们想要的就是:

  • 让该先行的逻辑先行——排任务优先级
  • 让浏览器能尽量在每一帧中都可以进行页面绘制——想办法让 JavaScript 的执行不影响到绘制任务

解决方法:

  • 优先级:我们给每类任务定义不同的优先级,来决定谁先上车(进 16ms),假设就是如下这种形式:
const FetchTask = {tag: 'sideEffect',priority: 'high'
}const ComputeTask = {tag: 'compute',priority: 'middle'
}const DomTask = {tag: 'dom-update',priority: 'low'
}

然后,为了

  • 合理布置逻辑的运行
  • 尽量不阻塞浏览器的绘制,最大限度的减少的掉帧的可能

fiber就是发挥作用了。

fiber设计原则

我们提炼一下官方给出的一些相关内容,大致可得: