React15架构

React15架构可以分为两层:

  • Reconciler(协调器)—— 负责找出变化的组件
  • Renderer(渲染器)—— 负责将变化的组件渲染到页面上

    Reconciler(协调器)

    我们知道,在React中可以通过this.setState、this.forceUpdate、ReactDOM.render等API触发更新。
    每当有更新发生时,Reconciler会做如下工作:

  • 调用函数组件、或class组件的render方法,将返回的JSX转化为虚拟DOM

  • 将虚拟DOM和上次更新时的虚拟DOM对比
  • 通过对比找出本次更新中变化的虚拟DOM
  • 通知Renderer将变化的虚拟DOM渲染到页面上

    Renderer(渲染器)

    由于React支持跨平台,所以不同平台有不同的Renderer。我们前端最熟悉的是负责在浏览器环境渲染的Renderer —— ReactDOM(opens new window)
    除此之外,还有:

  • ReactNative(opens new window)渲染器,渲染App原生组件

  • ReactTest(opens new window)渲染器,渲染出纯Js对象用于测试
  • ReactArt(opens new window)渲染器,渲染到Canvas, SVG 或 VML (IE8)

在每次更新发生时,Renderer接到Reconciler通知,将变化的组件渲染在当前宿主环境。

React16架构

React16架构可以分为三层:

  • Scheduler(调度器)—— 调度任务的优先级,高优任务优先进入Reconciler
  • Reconciler(协调器)—— 负责找出变化的组件
  • Renderer(渲染器)—— 负责将变化的组件渲染到页面上

可以看到,相较于React15,React16中新增了Scheduler(调度器)—- requestIdelCallback 的 polyfill


Scheduler(调度器)

既然我们以浏览器是否有剩余时间作为任务中断的标准,那么我们需要一种机制,当浏览器有剩余时间时通知我们。
其实部分浏览器已经实现了这个API,这就是requestIdleCallback(opens new window)。但是由于以下因素,React放弃使用:

  • 浏览器兼容性
  • 触发频率不稳定,受很多因素影响。比如当我们的浏览器切换tab后,之前tab注册的requestIdleCallback触发的频率会变得很低

基于以上原因,React实现了功能更完备的requestIdleCallbackpolyfill,这就是Scheduler。除了在空闲时触发回调的功能外,Scheduler还提供了多种调度优先级供任务设置。

Reconciler(协调器)

每次循环都会调用shouldYield判断当前是否有剩余时间。

  1. /** @noinline */
  2. function workLoopConcurrent() {
  3. // Perform work until Scheduler asks us to yield
  4. while (workInProgress !== null && !shouldYield()) {
  5. workInProgress = performUnitOfWork(workInProgress);
  6. }
  7. }

在React16中,ReconcilerRenderer不再是交替工作。当Scheduler将任务交给Reconciler后,Reconciler会为变化的虚拟DOM打上代表增/删/更新的标记,类似这样:

  1. export const Placement = /* */ 0b0000000000010;
  2. export const Update = /* */ 0b0000000000100;
  3. export const PlacementAndUpdate = /* */ 0b0000000000110;
  4. export const Deletion = /* */ 0b0000000001000;

更新流程

image.png
其中红框中的步骤随时可能由于以下原因被中断:

  • 有其他更高优任务需要先更新
  • 当前帧没有剩余时间

由于红框中的工作都在内存中进行,不会更新页面上的DOM,所以即使反复中断,用户也不会看见更新不完全的DOM。