REACT 更新机制

一、React 异步更新

当涉及到 React 中的异步更新时,有几个重要的概念需要了解。

  1. 批处理(Batching): React 会将多次状态更新合并为一个批处理,然后一次性应用到虚拟 DOM 上。这种批处理的机制确保了在同一帧中只进行一次渲染,以避免不必要的重复渲染。

  2. 异步处理: React 将不同的任务划分为优先级,例如用户交互属于高优先级任务,而渲染则是低优先级任务。React 会先处理高优先级任务,然后再处理低优先级任务,这样可以确保用户交互的响应性。

  3. 任务优先级调度: React 的 Fiber 架构引入了任务优先级调度的概念。Fiber 使得 React 可以以更细粒度地方式进行工作,并且可以根据任务的优先级来决定何时中断任务和恢复任务。

  4. setState 和异步更新: 当你使用类组件时,调用 setState 并不会立即触发 UI 更新。而是将更新加入到待处理的队列中,React 会在合适的时间执行更新。另外,setState 支持传递一个回调函数作为参数,在状态更新后执行。

    this.setState({ count: this.state.count + 1 }, () => {
      console.log("State updated:", this.state.count);
    });
    
  5. Hooks 中的异步更新: 在函数组件中,使用 Hooks 也会涉及到异步更新。例如,在 useEffect 钩子中执行的副作用,会在渲染完成后执行,而不会阻塞渲染。

  6. React 的协调过程: React 的异步更新和协调过程是紧密相关的。当状态发生变化时,React 会通过对比虚拟 DOM 树来确定需要更新的部分,并在合适的时间点进行更新。这个过程称为“协调”。

总之,React 的异步更新机制使得 UI 更新变得高效且响应快速,同时避免了不必要的重复工作。在大多数情况下,你不需要显式地管理异步更新,因为 React 已经为你处理了大部分细节。

二、REACT FIBER

当我们在 React 中创建组件并更新页面时,React 实际上在内部执行了一系列操作,以确保页面按照我们的预期进行渲染。React Fiber 是一种新的渲染引擎,对这些操作进行了重构和优化,以提高渲染性能和用户体验。

以下是 React Fiber 的更详细解释:

  1. 渲染过程的改进: 在旧的 React 渲染引擎中,渲染是递归执行的,而且一旦开始渲染就无法中断。这可能导致一些性能问题,特别是在大型应用中或者在执行复杂的计算时。React Fiber 引入了可中断和恢复的渲染,将渲染过程分割成小的单元,可以在执行过程中中断并处理其他任务,然后恢复渲染。

  2. 任务优先级和调度: React Fiber 允许开发者为不同任务设置优先级,如用户交互任务可以有更高的优先级。调度器会根据任务的优先级来决定任务的执行顺序,以确保用户交互的响应性。例如,当用户点击按钮时,React Fiber 会暂停正在进行的渲染,立即处理用户交互任务,然后再继续渲染。

  3. 任务分片(Time Slicing): Fiber 允许将一个大的任务分成多个小的片段,以便在任务之间进行调度。这可以避免长时间的渲染阻塞,提高了页面的响应性。每个任务片段在每一帧中被处理,以平滑地分散计算工作。

  4. 异步渲染: React Fiber 支持异步渲染,这意味着渲染可以在浏览器空闲的时间内进行,不会阻塞主线程。这有助于避免用户界面的卡顿,提高了应用的性能和流畅性。

  5. 调度算法: React Fiber 的调度算法负责决定任务的执行顺序,以及在何时中断和恢复任务。它采用了一种基于优先级和时间的动态调度策略,以提供最佳的用户体验和渲染性能。

React Fiber 是一项底层的改进,它的引入对开发者来说是透明的。开发者无需直接操作 Fiber,React 的内部机制会自动处理任务的调度和执行。然而,通过提高渲染性能和页面的响应性,React Fiber 为开发大型、复杂应用提供了更好的基础。

2.1 fiber node 节点

在 React 中,Fiber 是一个重要的概念,用于描述组件在渲染过程中的状态和结构。Fiber 数据结构的设计使得 React 能够更好地管理组件的渲染、更新和调度。

在 React 内部,每个组件都会被表示为一个 Fiber 节点。这些节点组成了一个树状结构,称为 Fiber 树,用于描述整个组件树的状态和关系。每个 Fiber 节点都包含了有关组件的各种信息,同时也包括了用于构建和调度渲染任务的必要数据。

一个 Fiber 节点通常包含以下重要的信息:

  1. type:组件的类型,可以是函数组件、类组件或原生 DOM 元素。

  2. stateNode:组件实例或 DOM 元素的引用。

  3. return:指向父级 Fiber 节点,构成了 Fiber 树的结构。

  4. child:指向第一个子级 Fiber 节点。

  5. sibling:指向下一个兄弟级 Fiber 节点。

  6. alternate:用于在更新过程中保存前一次渲染的 Fiber 节点,以支持渐进式更新和回退。

  7. effectTag:用于表示当前 Fiber 节点需要执行的操作,如插入、更新或删除等。

  8. props:组件的属性。

  9. pendingProps:等待更新的属性。

  10. memoizedProps:上一次渲染的属性。

  11. updateQueue:保存组件的状态更新信息。

  12. pendingWorkPriority:表示当前 Fiber 节点的优先级,用于任务调度。

  13. actualDuration:上一次渲染的实际持续时间。

  14. treeBaseDuration:树的基准渲染时间。

  15. stateNode:用于保存组件实例、DOM 元素或其他相关对象的引用。

  16. dependencies:用于处理副作用的依赖关系。

Fiber 节点的设计使得 React 能够更好地管理组件的更新和调度,以支持异步渲染、任务优先级、任务中断等特性,从而提高应用的性能和用户体验。在开发过程中,开发者通常不需要直接操作 Fiber 节点,React 内部会自动处理这些操作。

2.2 fiber 树

Fiber 树的构建和更新过程是通过递归和循环的方式实现的。在 React 中,渲染过程会从根 Fiber 节点开始,逐级构建 Fiber 树,同时处理组件的更新和调度。Fiber 树的设计使得 React 能够灵活地控制渲染优先级,实现局部更新,从而提高应用的性能和响应能力。

需要注意的是,开发者在使用 React 进行开发时,通常不需要直接操作 Fiber 树,React 内部会自动处理这些操作,使得开发者可以专注于组件的构建和业务逻辑。

2.3 任务优先级和调度

任务优先级和调度在 React 中是为了实现异步渲染、优化性能以及提高用户体验而引入的重要概念。

任务优先级:

任务优先级是 React 为不同任务分配的优先级级别,用于决定哪些任务应该先执行。React 将任务分为以下几个优先级:

  1. Immediate Priority(最高优先级):表示需要立即执行的任务,通常用于处理用户交互和动画。

  2. User Blocking Priority(用户阻塞优先级):表示需要尽快执行的任务,以保障用户体验,例如处理用户输入。

  3. Normal Priority(普通优先级):表示一般情况下需要执行的任务,比如组件的渲染更新。

  4. Low Priority(低优先级):表示优先级较低的任务,通常用于一些后台计算和数据预取等。

任务优先级可以根据不同场景和需求进行调整,以保证用户交互的流畅性和应用性能。

调度:

调度是指根据任务优先级安排任务的执行顺序。在 React 中,调度是由调度器(Scheduler)负责的,它会根据任务的优先级来决定哪些任务应该被优先执行,哪些任务可以被推迟执行,以及如何合理分配资源。

调度器采用了协作式的调度方式,即在任务执行过程中可以随时中断并切换到其他任务,从而实现优先级的切换和任务的中断。这种调度方式避免了长时间的任务占用主线程,提高了页面的响应性。

React 使用 Fiber 架构来实现调度。Fiber 架构通过将任务划分为多个小的单元(Fiber 节点)来实现任务的中断和切换,从而使得任务的调度更加灵活和可控。React Fiber 使得开发者可以通过任务的优先级和调度,实现更加细粒度的任务控制,提高应用的性能和用户体验。

总之,任务优先级和调度是 React 实现异步渲染和性能优化的重要手段,通过合理分配资源和执行顺序,使得应用能够更加高效地响应用户操作和更新数据。

2.4 任务分片(Time Slicing)

任务分片(Time Slicing)是 React Fiber 架构中的一个重要概念,用于将单个大型任务分成多个小的任务片段,从而使得长时间的任务可以被分解为多次执行,保证页面的响应性和性能。

在传统的同步渲染中,如果一个任务很耗时,会阻塞主线程的执行,导致页面卡顿,用户体验下降。而使用任务分片的方式,React Fiber 将一个大的任务分成多个小的任务单元(Fiber 节点),并且在执行这些任务单元之间进行切换,从而实现任务的中断和切换,以保证其他任务的执行。

任务分片的原理是通过将任务划分为多个优先级较低的任务片段,然后按照一定的优先级规则来执行这些任务片段。当一个任务片段执行完毕或达到时间片限制时,React Fiber 会暂停当前任务,切换到下一个任务片段执行,然后在下一个帧继续执行之前的任务片段。这样就实现了长时间任务的分割和交替执行,避免了主线程的阻塞。

任务分片在 React 中的应用场景包括:

  1. 组件的渲染更新:将组件的渲染工作分成多个小的任务片段,避免阻塞主线程,提高页面的响应性。

  2. 数据加载和异步操作:将数据加载和异步操作分成多个任务片段,使得数据的加载不会影响页面的交互。

  3. 动画和用户交互:将动画和用户交互分成小的任务片段,保证动画的流畅性和用户体验。

任务分片使得 React 应用可以更好地利用浏览器的空闲时间,优化任务的执行顺序,提高页面的渲染性能和用户体验。这也是 Fiber 架构的一个重要特性,为 React 提供了更高的灵活性和性能优化的能力。

2.5 调度算法

调度算法是计算机科学中用于管理多个任务或进程的执行顺序的方法。不同的调度算法可以影响系统的性能、资源利用率以及用户体验。以下是一些常见的调度算法:

  1. 先来先服务调度(First-Come, First-Served):按照任务到达的顺序进行执行,即先到达的任务先执行,适用于短作业和长作业混合的情况。

  2. 最短作业优先调度(Shortest Job Next):选择下一个执行的任务时,优先选择执行时间最短的任务,可最大程度减少平均等待时间。

  3. 优先级调度(Priority Scheduling):每个任务分配一个优先级,根据优先级选择下一个执行的任务,适用于有紧急任务的情况。

  4. 时间片轮转调度(Round Robin Scheduling):每个任务被分配一个固定时间片,在时间片内执行,超时后放入队尾,适用于多任务环境。

  5. 多级反馈队列调度(Multilevel Feedback Queue Scheduling):将任务分为多个队列,按优先级或时间片进行调度,任务在队列之间移动。

  6. 最高响应比优先调度(Highest Response Ratio Next):计算任务的响应比(等待时间+服务时间/服务时间),选择响应比最高的任务执行。

  7. 最早截止时间优先调度(Earliest Deadline First):根据任务的截止时间选择下一个执行的任务,以确保最早截止的任务得到执行。

  8. 最佳适应性调度(Best-Fit Scheduling):根据资源需求选择能够最佳适应的资源分配给任务,可减少资源浪费。

这些调度算法在不同的应用场景和系统环境下有不同的优缺点。选择合适的调度算法可以有效地提高系统的性能和资源利用率。

2.6 并发模式(Concurrent Mode)

React 的 Concurrent Mode 是一种新的渲染模式,旨在提高 React 应用的性能和用户体验。它是在 React 16 版本中引入的,通过一系列的优化和改进来实现更好的并发处理能力,特别是在处理复杂 UI 和长时间任务时。

Concurrent Mode 的主要特点和目标包括:

  1. 时间分片(Time Slicing):Concurrent Mode 使用时间分片技术,将长时间运行的任务分成小的时间片。这些时间片可以在执行过程中中断,从而使浏览器有更多的机会来响应用户输入或执行其他任务。这样可以保持应用的响应性,避免阻塞主线程。

  2. 任务优先级(Priority Scheduling):Concurrent Mode 引入了任务优先级的概念,允许 React 根据任务的重要性来调度任务的执行顺序。高优先级的任务会优先执行,从而保证关键任务的及时完成。

  3. 协作式调度(Cooperative Scheduling):在 Concurrent Mode 下,任务之间进行协作式调度,可以让出主线程给更高优先级的任务,从而提高了任务的调度效率。

  4. 增量渲染(Incremental Rendering):Concurrent Mode 可以实现增量渲染,即将渲染工作分成多个步骤,逐步完成。这样可以更快地显示初始内容,然后逐步补充细节,提高用户感知的渲染速度。

  5. 优雅的中断和恢复:因为任务是分片的,所以在进行中的任务可以中断,而后续的任务可以继续执行。这种特性使得应用能够更好地适应用户的交互和其他突发情况。

Concurrent Mode 并不是一个全新的 React 版本,而是在现有的 React 库中引入的一种渲染模式和架构优化。要使用 Concurrent Mode,您可以通过将 concurrentMode 标志设置为 true 来启用,例如:

import { unstable_createRoot } from "react-dom";

const root = unstable_createRoot(document.getElementById("root"));
root.render(<App />);

总之,React 的 Concurrent Mode 旨在提供更好的并发处理能力,通过时间分片、任务优先级、协作式调度等技术,提高应用的性能和响应性。这使得 React 应用能够更好地处理复杂 UI 和长时间任务,提供更好的用户体验。

Contributors: masecho