Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

React架构 #27

Open
isNeilLin opened this issue Jun 18, 2021 · 0 comments
Open

React架构 #27

isNeilLin opened this issue Jun 18, 2021 · 0 comments
Labels
框架&工具库 Vue和React等前端框架或工具库相关

Comments

@isNeilLin
Copy link
Owner

isNeilLin commented Jun 18, 2021

查看原文

React15架构

React15架构可以分为两层:

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

#Reconciler(协调器)

我们知道,在React中可以通过this.setStatethis.forceUpdateReactDOM.render等API触发更新。

每当有更新发生时,Reconciler会做如下工作:

  • 调用函数组件、或class组件的render方法,将返回的JSX转化为虚拟DOM
  • 将虚拟DOM和上次更新时的虚拟DOM对比
  • 通过对比找出本次更新中变化的虚拟DOM
  • 通知Renderer将变化的虚拟DOM渲染到页面上

你可以在这里 (opens new window)看到React官方对Reconciler的解释

#Renderer(渲染器)

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

除此之外,还有:

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

你可以在这里 (opens new window)看到React官方对Renderer的解释

#React15架构的缺点

Reconciler中,mount的组件会调用mountComponent (opens new window)update的组件会调用updateComponent (opens new window)。这两个方法都会递归更新子组件。

#递归更新的缺点

由于递归执行,所以更新一旦开始,中途就无法中断。当层级很深时,递归更新时间超过了16ms,用户交互就会卡顿。

在上一节中,我们已经提出了解决办法——用可中断的异步更新代替同步的更新。那么React15的架构支持异步更新么?让我们看一个例子:

乘法小Demo

我用红色标注了更新的步骤。 更新流程

我们可以看到,ReconcilerRenderer是交替工作的,当第一个li在页面上已经变化后,第二个li再进入Reconciler

由于整个过程都是同步的,所以在用户看来所有DOM是同时更新的。

接下来,让我们模拟一下,如果中途中断更新会怎么样?

注意

以下是我们模拟中断的情况,实际上React15并不会中断进行中的更新

中断更新流程

当第一个li完成更新时中断更新,即步骤3完成后中断更新,此时后面的步骤都还未执行。

用户本来期望123变为246。实际却看见更新不完全的DOM!(即223

基于这个原因,React决定重写整个架构。

React16架构

React16架构可以分为三层:

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

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

#Scheduler(调度器)

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

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

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

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

Scheduler (opens new window)是独立于React的库

#Reconciler(协调器)

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

我们可以看见,更新工作从递归变成了可以中断的循环过程。每次循环都会调用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;

全部的标记见这里(opens new window)

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

你可以在这里 (opens new window)看到React官方对React16新Reconciler的解释

#Renderer(渲染器)

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

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

乘法小Demo

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

更新流程

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

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

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

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

#总结

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

Reconciler内部采用了Fiber的架构。

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

#参考资料

「英文 外网」Building a Custom React Renderer | React前经理Sophie Alpert(opens new window)

同步/Debounce/Throttle/并发 情况下性能对比Demo

为该章节纠错 (opens new window)

上次更新: 3/22/2021, 3:01:33 PM

老的React架构Fiber架构的心智模型

@isNeilLin isNeilLin added the 框架&工具库 Vue和React等前端框架或工具库相关 label Jun 18, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
框架&工具库 Vue和React等前端框架或工具库相关
Projects
None yet
Development

No branches or pull requests

1 participant