在探究 React Fiber 之前,我们必须理解浏览器的渲染机制。主流浏览器通常以 60Hz 的频率刷新,即每 16.6 毫秒刷新一次页面。此外,浏览器有一个主线程,负责执行 JavaScript 代码和页面渲染(布局、绘制、图层合并)。由于 JavaScript 操作 DOM 与 GUI 渲染线程互斥,因此它们无法同时执行。
在 React 15 中,每次调用 this.setState
都会重新渲染整个组件树,因此在 componentDidMount
中调用 this.showList
会触发包含 10000 个元素的完整渲染。这导致大量的 JavaScript 运算占据主线程,页面卡顿,用户输入也无法响应。
相比之下,在 React 16 及更高版本中引入了 Fiber 架构。当调用 this.setState
时,React 使用异步更新策略,将更新拆分成多个小块,在多个事件循环周期内完成。这样可以先渲染高优先级的更新(如用户输入操作),避免大量 DOM 操作阻塞主线程。Fiber 好比为 React 加上了操作系统,告诉它何时进行 diff、渲染和响应用户输入。这种机制类似于操作系统的时间片轮转法,也类似于 generator 允许中断的机制。
因此,了解 React15 到 React16 架构的变化以及 Fiber 的设计原理对于提升性能至关重要。
Reconciler(协调器):负责确定哪些组件发生了变化。
Renderer(渲染器):负责将发生变化的组件渲染到页面上。
Reconciler 扮演着决策者的角色,它会执行以下任务:
Renderer 负责将发生变化的组件渲染到当前的宿主环境中。在前端开发中,我们通常使用的是 ReactDOM 渲染器,它负责将变化的虚拟 DOM 渲染到浏览器环境中。
每当 Reconciler 发现有组件发生变化时,它会通知 Renderer 进行渲染,将变化的内容呈现在页面上。
通过 Reconciler 和 Renderer 的配合,React15 架构完成了组件的更新和页面的渲染。
在 React15 架构中,存在一些明显的缺点,主要集中在 Reconciler 部分:
在上述例子中,Reconciler 和 Renderer 交替工作,但整个更新过程都是同步的。因此,即使在页面上的第一个列表项已经更新完成,如果更新过程尚未结束,用户输入的内容也无法立即响应。
这些问题使得 React15 架构在处理大型组件树或需要频繁更新的场景下性能不佳,因此,React 团队推出了新的架构 —— Fiber,来解决这些问题并提升性能。
React16 架构相比于 React15,引入了新的调度器(Scheduler),让我们来逐层了解 React16 架构的组成部分。
Scheduler 的引入是为了解决任务调度的问题。它的主要功能包括:
在 React16 中,Reconciler 的工作方式发生了重大变化。主要的改进包括:
Renderer 负责将 Reconciler 标记的变化渲染到页面上。主要的改进包括:
通过 Scheduler、Reconciler 和 Renderer 三个层次的协作,React16 架构实现了更高效、更稳定的组件更新和页面渲染过程,极大地提升了 React 应用的性能和用户体验。
在 React15 及之前的版本中,调和器(Reconciler)采用递归方式创建虚拟 DOM,这意味着递归过程无法中断。当组件树很深时,递归会占用大量线程时间,导致页面卡顿。
为了解决这个问题,React16 引入了一种异步的可中断更新机制,重构了递归更新。由于之前用于递归的虚拟 DOM 数据结构无法满足需求,因此全新的 Fiber 架构应运而生。
Fiber 具有三层含义:
React 中同时存在两棵 Fiber 树:current Fiber 树和 workInProgress Fiber 树。当前屏幕上显示内容对应的 Fiber 树称为 current Fiber 树,正在内存中构建的 Fiber 树称为 workInProgress Fiber 树。current 和 workInProgress 通过 alternate 属性连接。
首次执行 ReactDOM.render 会创建 fiberRoot 和 rootFiber。fiberRoot 是整个应用的根节点,rootFiber 是所在组件树的根节点。fiberRoot 的 current 指针指向当前页面上已渲染内容对应 Fiber 树,即 current Fiber 树。接着进入 render 阶段,根据组件返回的 JSX 在内存中创建 Fiber 节点并构建 Fiber 树,被称为 workInProgress Fiber 树。在构建 workInProgress Fiber 树时会尝试复用 current Fiber 树中已有的 Fiber 节点内的属性。渲染完毕后,fiberRoot 的 current 指针指向 workInProgress Fiber 树,使其成为 current Fiber 树。
触发状态改变后,会开启一次新的 render 阶段并构建一棵新的 workInProgress Fiber 树。同样地,workInProgress Fiber 的创建可以复用 current Fiber 树对应的节点数据。完成构建后,workInProgress Fiber 树进入 commit 阶段渲染到页面上。渲染完毕后,workInProgress Fiber 树变为 current Fiber 树。
这样,React 通过双缓存 Fiber 树实现了灵活的更新机制,提高了页面性能和用户体验。
Fiber 架构的引入为 React 的更新机制带来了革命性的改变,使得 React 应用能够更加流畅地响应用户操作,提升了整体性能。
React16 引入了 Scheduler(调度器)和 Fiber 两个重要的架构,它们共同为 React 应用的性能和用户体验提供了显著的改进。
Scheduler 调度器
Scheduler 的作用是协调任务的时间线,优化任务的执行顺序。它能够根据任务的优先级和其他条件来决定任务的执行顺序,从而最大程度地提升页面的渲染性能和用户交互体验。
Fiber 架构
Fiber 是 React 中重新实现的核心算法,主要目的是使渲染过程变得异步化。通过 Fiber 架构,React 能够实现暂停、恢复和优先级调度等功能,从而更好地响应用户操作,提高页面加载速度和流畅度。
协同工作
Scheduler 和 Fiber 两者协同工作,Scheduler 负责调度任务的执行顺序,而 Fiber 负责实现异步渲染和任务的中断与恢复。这种协同工作的机制使得 React 具备了更高的灵活性和响应能力,能够更好地适应不同场景下的需求。
通过引入 Scheduler 和 Fiber,React16 实现了异步渲染、优先级调度等功能,极大地提升了页面加载速度和用户交互体验,是一次重要的性能提升。