React16 架构可以分为三层:
- Scheduler(调度器)—— 调度任务的优先级,高优任务优先进入 Reconciler
- Reconciler(协调器)—— 负责找出变化的组件
- Renderer(渲染器)—— 负责将变化的组件渲染到页面上
可以看到,相较于 React15,React16 中新增了 Scheduler(调度器),让我们来了解下他。
Scheduler(调度器)
既然我们以浏览器是否有剩余时间作为任务中断的标准,那么我们需要一种机制,当浏览器有剩余时间时通知我们。
其实部分浏览器已经实现了这个 API,这就是requestIdleCallback (opens new window)。但是由于以下因素,React
放弃使用:
- 浏览器兼容性
- 触发频率不稳定,受很多因素影响。比如当我们的浏览器切换 tab 后,之前 tab 注册的
requestIdleCallback
触发的频率会变得很低
基于以上原因,React
实现了功能更完备的requestIdleCallback
polyfill,这就是 Scheduler。除了在空闲时触发回调的功能外,Scheduler 还提供了多种调度优先级供任务设置。
Scheduler (opens new window)是独立于
React
的库
Reconciler(协调器)
我们知道,在 React15 中 Reconciler 是递归处理虚拟 DOM 的。让我们看看React16 的 Reconciler (opens new window)。
我们可以看见,更新工作从递归变成了可以中断的循环过程。每次循环都会调用shouldYield
判断当前是否有剩余时间。
1 | /** @noinline */ |
那么 React16 是如何解决中断更新时 DOM 渲染不完全的问题呢?
在 React16 中,Reconciler 与 Renderer 不再是交替工作。当 Scheduler 将任务交给 Reconciler 后,Reconciler 会为变化的虚拟 DOM 打上代表增/删/更新的标记,类似这样:
1 | export const Placement = /* */ 0b0000000000010 |
全部的标记见这里(opens new window)
整个 Scheduler 与 Reconciler 的工作都在内存中进行。只有当所有组件都完成 Reconciler 的工作,才会统一交给 Renderer。
你可以在这里 (opens new window)看到
React
官方对 React16 新 Reconciler 的解释
Renderer(渲染器)
Renderer 根据 Reconciler 为虚拟 DOM 打的标记,同步执行对应的 DOM 操作。
所以,对于我们在上一节使用过的 Demo
在 React16 架构中整个更新流程为:
其中红框中的步骤随时可能由于以下原因被中断:
- 有其他更高优任务需要先更新
- 当前帧没有剩余时间
由于红框中的工作都在内存中进行,不会更新页面上的 DOM,所以即使反复中断,用户也不会看见更新不完全的 DOM(即上一节演示的情况)。
实际上,由于 Scheduler 和 Reconciler 都是平台无关的,所以
React
为他们单独发了一个包react-Reconciler (opens new window)。你可以用这个包自己实现一个ReactDOM
,具体见参考资料
参考资料
「英文 外网」Building a Custom React Renderer | React 前经理 Sophie Alpert