返回介绍

新的 React 架构

发布于 2020-12-15 07:47:19 字数 4096 浏览 1567 评论 2 收藏 0

上一节我们聊到 React15 架构不能支撑异步更新以至于需要重构。那么这一节我们来学习重构后的 React16 是如何支持异步更新的。

React16 架构

React16架构可以分为三层:

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

可以看到,相较于React15,React16中新增了Scheduler(调度器),让我们来了解下他。

Scheduler(调度器)

既然我们以浏览器是否有剩余时间作为任务中断的标准,那么我们需要一种机制,当浏览器有剩余时间时通知我们。

其实部分浏览器已经实现了这个API,这就是requestIdleCallback。但是由于以下因素,React放弃使用:

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

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

Scheduler是独立于React的库

Reconciler(协调器)

我们知道,在 React15 中Reconciler是递归处理虚拟DOM的。让我们看看React16的Reconciler

我们可以看见,更新工作从递归变成了可以中断的循环过程。每次循环都会调用shouldYield判断当前是否有剩余时间。

/** @noinline */
function workLoopConcurrent() {
  // Perform work until Scheduler asks us to yield
  while (workInProgress !== null && !shouldYield()) {
    workInProgress = performUnitOfWork(workInProgress);
  }
}

那么React16是如何解决中断更新时DOM渲染不完全的问题呢?

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

export const Placement = /*             */ 0b0000000000010;
export const Update = /*                */ 0b0000000000100;
export const PlacementAndUpdate = /*    */ 0b0000000000110;
export const Deletion = /*              */ 0b0000000001000;

全部的标记见这里

整个SchedulerReconciler的工作都在内存中进行。只有当所有组件都完成Reconciler的工作,才会统一交给Renderer

你可以在这里看到React官方对React16新Reconciler的解释

Renderer(渲染器)

Renderer根据Reconciler为虚拟DOM打的标记,同步执行对应的DOM操作。

所以,对于我们在上一节使用过的Demo

在React16架构中整个更新流程为:

更新流程

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

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

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

实时上,由于SchedulerReconciler都是平台无关的,所以React为他们单独发了一个包react-Reconciler。你可以用这个包自己实现一个ReactDOM,具体见参考资料

总结

通过本节我们知道了React16采用新的Reconciler

Reconciler内部采用了Fiber的架构。

Fiber是什么?他和Reconciler或者说和React之间是什么关系?我们会在接下来三节解答。

参考资料

「英文 外网」Building a Custom React Renderer | React 前经理 Sophie Alpert

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

JSmiles 回复 wanglibo 2020-12-31 14:15:09

使用 ConcurrentMode 组件包裹的子组件的渲染过程的优先级会被降低,react 会先渲染优先级高的,然后将 js 线程空闲出来先干其他的事,如动画的渲染,完了之后再渲染优先级低的,当我们想提高子组件渲染的优先级的时候,可以使用 flushSync 方法来包裹需要进行的操作。

wanglibo 2020-12-28 19:04:00

ConcurrentMode 不是已经被移除了吗

export type TypeOfMode = number;
export const NoMode = 0b00000;
export const StrictMode = 0b00001;
// TODO: Remove BlockingMode and ConcurrentMode by reading from the root
// tag instead
export const BlockingMode = 0b00010;
export const ConcurrentMode = 0b00100;
export const ProfileMode = 0b01000;
export const DebugTracingMode = 0b10000;
~没有更多了~
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文