美文网首页
React15 和 React16 的架构对比

React15 和 React16 的架构对比

作者: 弱冠而不立 | 来源:发表于2021-01-20 14:58 被阅读0次

推荐一篇好文章:React技术揭秘

基础知识

JS 是单线程的,浏览器是多线程的

对于多线程的浏览器而言,它除了要处理 JS这个线程,它还要处理定时器,网络请求,UI渲染...这些线程。
而 JavaScript 线程是可以操作 DOM 的。这就意味着渲染线程和 JavaScript 线程同时在工作,那么渲染结果必然是难以预测的:比如渲染线程刚绘制好的画面,可能转头就会被一段 JavaScript 给改得面目全非。这就决定了JavaScript 线程和渲染线程必须是互斥的:这两个线程不能够穿插执行,必须串行。当其中一个线程执行时,另一个线程只能挂起等待。
在这样的机制下,若 JavaScript 线程长时间地占用了主线程,那么渲染层面的更新就不得不长时间地等待,界面长时间不更新,带给用户的体验就是所谓的“卡顿”。

那么JS线程占用时间多少合适呢?

现在市场上主流的屏幕刷新率都是60HZ(一秒刷新60次),也就是(1000ms / 60)。即16.6ms浏览器刷新一次。在每16.6ms时间内,需要完成JS脚本执行 ----- 样式布局 ----- 样式绘制。当JS执行时间过长,超出该帧,那么这次刷新就没有时间执行样式布局和样式绘制了。

React 15 时期的渲染机制

15版本是基于Stack Reconcilation(栈调和器)。它是递归、同步的方式。栈的优点在于用少量的代码就可以实现diff功能。并且非常容易理解。但是它也带来了严重的性能问题。

解释一下调和器的概念

“调和”又译为“协调”,协调过程的官方定义,藏在 React 官网对Virtual DOM 及内核 这一概念的解释中,原文如下:

Virtual DOM 是一种编程概念。在这个概念里, UI 以一种理想化的,或者说“虚拟的”表现形式被保存于内存中,并通过如 ReactDOM 等类库使之与“真实的” DOM 同步。这一过程叫做协调

这段话里的重点:通过如 ReactDOM 等类库使虚拟 DOM 与“真实的” DOM 同步这一过程叫作协调(调和)。
调和器所做的工作是一系列的,包括组件的挂载、卸载新等过程,其中更新过程涉及对 Diff 算法的调用。其中,我们需要注意 调和 !== Diffing。Diff 仅是调和过程中最具代表性的一环。
根据 Diff 实现形式的不同,调和过程被划分为了以 React 15 为代表的“栈调和”以及 React 16 的“Fiber 调和”。但在实际的面试过程中,当面试官抛出 Reconciliation 相关问题时,也多半是为了了解候选人对 Diff 的掌握程度。

React15时期架构的缺点

React15架构可以分为两层:

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

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

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

而React15使用的是栈调和器,由于递归执行,所以更新一旦开始,中途就无法中断。当调用层级很深时,递归更新时间超过了屏幕刷新时间间隔,用户交互就会卡顿。

React16架构概览

React16架构可以分为三层:

  • Scheduler(调度器)—— 调度任务的优先级,高优任务优先进入Reconciler
  • Reconciler(协调器)—— 负责找出变化的组件
  • Renderer(渲染器)—— 负责将变化的组件渲染到页面上
相比React15,16增加了一个Scheduler(调度器),我们来了解一下。

通过上面基础知识已经了解,当JS执行时间过长,带给用户的体验就是所谓的“卡顿”。那么我们要如何解决这个问题呢?
答案是:在浏览器每一帧的时间中,预留一些时间给JS线程,React利用这部分时间更新组件(可以看到,在源码中,预留的初始时间是5ms)。
当预留的时间不够用时,React将线程控制权交还给浏览器使其有时间渲染UI,React则等待下一帧时间到来,继续被中断的工作。
既然我们以浏览器是否有剩余时间作为任务中断的标准,那么我们需要一种机制,当浏览器有剩余时间时通知我们。所以React就实现了一个Scheduler(调度器),除了在空闲时触发回调的功能外,Scheduler还提供了多种调度优先级供任务设置。

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);
  }
}

同时,我们需要注意,16中的更新是可中断的,那React如何解决要是中断了,DOM渲染不完全的问题呢?
在React16中,Reconciler与Renderer不再是严格同步的(就是说,不是一协调完一个就立刻通知Renderer去渲染)。而是当Scheduler将任务交给Reconciler后,Reconciler会为变化的虚拟DOM打上代表增/删/更新的标记,类似这样:

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

全部的标记见这里

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

在React16架构中整个更新流程
其中红框中的步骤随时可能由于以下原因被中断:
  • 其他更高优任务需要先更新
  • 当前帧没有剩余时间

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

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

总结

  • React15 使用的是栈调和器,栈调和器是递归执行调和操作的,更新一旦开始,中途就无法中断。倘若递归层级过深,则可能会造成浏览器渲染卡顿。
  • React16 使用的是全新的"Fiber调和器",这就依托于React16的重点了—Fiber架构。目前简要概括就是,React16能够实现中断调和,分批次异步地调和。从而达到不因为JS执行时间过久影响浏览器渲染。

相关文章

  • React15 和 React16 的架构对比

    推荐一篇好文章:React技术揭秘[https://react.iamkasong.com/] 基础知识 JS 是...

  • React-setState源码阅读

    本文基于React15总结,最新React16可能有一些出入,望周知!!! setState真的是异步的吗 ? 举...

  • [react] react15、react16生命周期对比

    一、react15生命周期 初始状态初始状态 点击“挂载Child Component”按钮后点击“挂载Child...

  • React16性能改善的原理(一)

    问题背景 React16 更新了底层架构,新架构主要解决更新节点过多时,页码卡顿的问题。譬如如下代码,根据用户输入...

  • React源码01 - 开篇

    本源码学习笔记始发:React 源码学习-yuque 1. React16 架构 React 是一个 UI 框架:...

  • React Fiber架构

    React为什么要推出Fiber架构? 在页面元素很多,还需要频繁刷新的情况下,React15会出现掉帧的现象。其...

  • react开发总结

    之前公司开发用react做了线上平台,仓促选型,使用react16进行开发,兼容性和架构问题上遇到了一些坑,特此做...

  • 在react中,fiber链表是怎么遍历的?

    Fiber是对react核心算法的重构,react16以上版本引入了fiber架构,其中的设计思想很值得我们去学习...

  • SOA架构和微服务架构的对比

    SOA架构和微服务架构的区别 首先SOA和微服务架构一个层面的东西,而对于ESB和微服务网关是一个层面的东西,一个...

  • ReactDom.render 源码阅读

    React16 源码简介 React16 重写了核心算法 reconciler。因为 JavaScript 引擎线...

网友评论

      本文标题:React15 和 React16 的架构对比

      本文链接:https://www.haomeiwen.com/subject/zksuaktx.html