新老react 架构对比

534 阅读3分钟

这是我参与8月更文挑战的第17天,活动详情查看:8月更文挑战

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通知,将变化的组件渲染在当前宿主环境。

React15 的缺点

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

递归更新的缺点

由于递归执行,所以更新一旦开始,中途就无法中断。当层级很深时,递归更新时间超过了16ms,用户交互就会卡顿。解决方法 ->用可中断的异步更新代替同步的更新。(react16)

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的库(目前Scheduler只在react当中使用)

Reconciler(协调器)

在React15中Reconciler是递归处理虚拟DOM的。React16的Reconciler更新工作从递归变成了可以中断的循环过程。每次循环都会调用shouldYield判断当前是否有剩余时间。

ReconcilerRenderer不再是交替工作。当Scheduler将任务交给Reconciler后,Reconciler会为变化的虚拟DOM打上代表增/删/更新的标记, 整个SchedulerReconciler的工作都在内存中进行。只有当所有组件都完成Reconciler的工作,才会统一交给Renderer

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

Renderer(渲染器)

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

总结

react 16 修正了react15的一些缺点,用用可中断的异步更新代替同步的更新,使快速响应成为可能