为什么需要 React Fiber

在React Fiber之前,React的渲染过程是基于递归的,它会在单个渲染周期内一直执行,直到渲染完成。这种方式在处理大型组件树或者复杂动画时可能会造成性能问题,因为递归渲染会阻塞主线程,导致页面卡顿

React Fiber 的出现就是为了解决这个问题。它引入了一种新的、可中断的渲染流程,使得React可以在渲染的过程中主动中断,执行其他任务,然后再继续渲染。这种方式使得React可以更好地响应用户交互,提供更好的用户体验。

Fiber 架构的特点和原理

总的来说就是将原来递归渲染的过程拆分为一个个小的、可中断的、存在优先级的单元,

  1. 可中断性(Interruptible): Fiber 架构允许React任务的中断和恢复。React Fiber将任务切分为多个小任务单元,可以在每个小任务单元之间进行中断和恢复。这种可中断性使得React能够更好地响应用户交互和其他优先级更高的任务。

  2. 优先级调度(Priority Scheduling): Fiber 架构引入了优先级的概念,不同类型的任务有不同的优先级。比如,用户交互相关的任务优先级较高,而像渲染长列表这样的任务优先级较低。Fiber 架构根据任务的优先级动态调整任务的执行顺序,以保证高优先级任务的及时响应。

  3. 增量渲染(Incremental Rendering): Fiber 架构实现了增量渲染,即将渲染过程拆分为多个步骤,每个步骤可以单独执行。这种方式使得React可以在每个步骤之间中断,执行其他任务,然后再继续渲染。增量渲染提高了React的渲染性能,特别是在处理大型组件树时。

  4. 协作式多任务处理(Cooperative Multitasking): Fiber 架构采用了协作式的多任务处理方式。在Fiber架构中,任务的切换是由React自身控制的,而不是由操作系统控制。这种方式使得React可以更好地控制任务的执行顺序,提高了性能和响应性。

每个组件对应Fiber节点是什么样的,都包含哪些信息

每个组件对应的 Fiber 节点是一个具有多个属性的数据结构,它包含了组件的相关信息以及用于协调更新和渲染的元数据。下面是一个典型的 Fiber 节点可能包含的一些信息:

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

  2. key: 用于帮助 React 识别组件的唯一性,通常用于优化渲染。

  3. props: 包含了组件的属性,包括从父组件传递下来的属性。

  4. stateNode: 表示 Fiber 节点对应的实际 DOM 元素,或者是组件的实例。

  5. child: 指向该组件的第一个子组件的 Fiber 节点。

  6. sibling: 指向该组件的下一个兄弟组件的 Fiber 节点。

  7. return: 指向该组件的父级组件的 Fiber 节点。

  8. alternate: 指向一个与当前 Fiber 节点相关的 Fiber 树的对应节点,用于双缓冲技术和协调更新。

  9. effectTag: 一个标志位,表示组件需要进行何种类型的更新,例如创建、删除、更新等。

  10. updateQueue: 包含了组件的更新队列,用于存储需要执行的状态更新操作。

这些信息构成了 Fiber 节点的一部分,React 使用这些信息来协调组件的更新和渲染过程。在双缓冲技术中,还有一个备用的 Fiber 树用于协调和更新,上面提到的 alternate 字段就用于与当前 Fiber 树相关的备用 Fiber 树。

React fiber的工作过程

React Fiber 是 React 内部的一种协调引擎,它改进了 React 的渲染和协调过程,使得 React 应用能够更好地处理多任务,提高了性能和响应速度。以下是 React Fiber 的详细工作过程:

1. 任务调度和构建 Fiber 树

1.1 任务调度: 当有任务(比如组件的状态更新)需要执行时,React 将任务放入调度队列中。

1.2 Fiber 树的构建: React Fiber 首先会创建一颗 Fiber 树,每个 Fiber 节点代表一个组件。在构建过程中,React 会根据任务的优先级和 Fiber 树的结构,确定任务的执行顺序。

2. 任务的分割和优先级调度

2.1 任务分割: 当React需要执行一个任务(比如组件的更新)时,这个任务会被分割成多个小的Fiber节点,每个节点代表一个子任务。React Fiber使用Fiber节点作为任务的最小执行单元,这种分割保证了任务的可中断性,即任务的执行可以在Fiber节点之间中断和恢复。

2.2 优先级调度: 每个任务都有一个优先级。React Fiber 根据任务的优先级决定何时执行哪个任务,这种调度机制提高了应用的响应速度。

3. 可中断性和任务的中断和恢复

3.1 可中断性: 任务的执行是可中断的,即在执行过程中可以被中断。

3.2 中断和恢复: 当更高优先级的任务到来时,React Fiber 可以暂停正在执行的任务,切换到更高优先级的任务,然后再回来继续执行之前中断的任务。这种中断和恢复的机制使得 React 能够在渲染任务之间灵活切换,提高了应用的响应性。

4. Fiber 树的协调和双缓冲

4.1 Fiber 树的协调: 在更新过程中,React Fiber 会比较新的 React 元素树和之前的 Fiber 树,找出需要更新的部分。为了防止在更新过程中影响到用户界面,React 使用双缓冲技术,即在两颗 Fiber 树之间切换。

4.2 双缓冲技术: 双缓冲技术确保了在更新过程中用户不会看到不稳定的界面。React Fiber 有两颗 Fiber 树,分别为”current tree”和”work-in-progress tree”。在更新完成后,React Fiber 将”work-in-progress tree”切换为”current tree”,用户才会看到更新后的内容,而在此过程中不会看到不稳定的中间状态。

5. 任务执行和副作用处理

5.1 任务执行: React Fiber 会遍历 Fiber 树,执行任务。任务的执行可能包括组件的函数调用、状态的更新、DOM 操作等。

5.2 副作用处理: 在执行过程中,React Fiber 会生成一个副作用链表(effect list),包含了所有需要执行的操作,比如插入、删除、更新等。这个链表记录了组件更新的副作用。

6. 提交阶段

6.1 提交阶段: 在提交阶段,React Fiber 会遍历副作用链表,执行其中的操作。这些操作会直接影响到实际的 DOM 结构,完成组件的更新。由于 React Fiber 使用了优先级调度和可中断性的特性,它能够在每一帧的时间片内完成尽可能多的工作,使得用户界面保持流畅响应,同时也避免了阻塞主线程。

综上所述,React Fiber 的工作过程通过引入可中断性、优先级调度、增量渲染等特性,实现了一种高效的渲染和更新机制。这种机制使得 React 应用能够在不同的任务之间灵活切换,提高了响应性和用户体验。

以上是React Fiber工作大体过程,接下来针对每个点展开说说。

任务调度

React Fiber 的任务调度是指确定何时执行哪个任务的过程。在 React Fiber 中,任务调度是基于优先级的,React Fiber 使用了一种称为“调度器”的机制来管理任务的执行顺序。以下是 React Fiber 中任务调度的详细说明:

1. 任务的优先级

React Fiber 将任务分为不同的优先级,不同类型的任务有不同的优先级。通常,用户交互相关的任务(例如鼠标点击事件、键盘输入事件)具有较高的优先级,而低优先级的任务(例如渲染、数据更新)则具有较低的优先级。React Fiber 定义了以下几种优先级:

  • Immediate(最高优先级): 用于处理用户交互事件,具有最高优先级,能够中断当前任务。
  • UserBlocking: 用于响应用户交互,但不能中断当前任务。
  • Normal(默认优先级): 用于普通的更新任务,低优先级,不会中断其他任务。
  • Low: 用于后台任务等低优先级的任务。

2. 任务调度器

React Fiber 使用一个任务调度器来确定任务的执行顺序。任务调度器负责管理任务的队列,根据任务的优先级来安排任务的执行顺序。React Fiber 的任务调度器采用了一个叫做“调度器阶段(Scheduler Phase)”的概念。

  • Work Loop(工作循环): 调度器阶段中的工作循环是一个循环结构,它负责检查任务队列中的任务,根据任务的优先级来决定执行哪个任务。

  • Expiration Time(过期时间): 每个任务都有一个过期时间,表示任务的截止执行时间。React Fiber 使用过期时间来比较任务的紧急程度,以确定任务的优先级。

在React Fiber中,任务调度器负责管理任务的队列。任务调度器使用一个数据结构,通常是一个最小堆(min heap)或者其他优化过的数据结构,来存储待执行的任务。这个数据结构根据任务的优先级(expiration time)来组织任务,确保具有较高优先级的任务在队列的顶部,从而能够快速找到下一个要执行的任务。

当一个任务被触发(例如组件状态更新、用户事件等)时,它会被创建成一个任务(或者叫做工作单元),并且带有一个过期时间(expiration time)。任务调度器将这个任务放入任务队列中,通常是根据任务的过期时间将任务插入到合适的位置,保持队列的有序性。

3. 任务的执行

任务调度器会在工作循环中选择一个高优先级的任务执行,如果当前任务的优先级比较低,可能会被中断,让出执行权给更高优先级的任务。这种中断和恢复的机制使得 React Fiber 能够在任务之间灵活切换,提高了应用的响应速度。

4. 任务的中断和恢复

React Fiber 具备任务的可中断性,即任务可以在执行过程中被中断,然后再恢复执行。当更高优先级的任务到来时,React Fiber 可以暂停正在执行的任务,切换到更高优先级的任务,然后再回来继续执行之前中断的任务。这种中断和恢复的机制使得 React 能够在渲染任务之间灵活切换,提高了应用的响应性。

以下是React Fiber中任务的中断和恢复的工作原理:

在React Fiber中,任务被分割成小单元(Fiber),这些小单元可以被中断和恢复。当任务执行时,React会在每个小单元之间设置中断点,允许任务在某个小单元执行完毕后被中断,而不需要等待整个任务执行完毕。这种分割和可中断性的机制是实现中断和恢复的基础。

当更高优先级的任务(比如用户输入事件)到达时,React Fiber可以中断正在执行的低优先级任务。在中断时,React Fiber会记住中断点,然后将执行权转交给更高优先级的任务。

一旦更高优先级的任务执行完毕,React Fiber会返回到之前中断的任务,并从中断点处继续执行。这种中断和恢复的机制允许React在不同任务之间进行动态切换,响应用户交互,同时保持对低优先级任务的处理。

综上所述,React Fiber 的任务调度是基于优先级的,通过任务的分割、优先级调度和可中断性,使得 React 应用能够在不同的任务之间进行灵活切换,提供更好的用户体验。

构建Fiber树

1. 组件的调度和更新触发

当组件的状态或属性发生变化,或者父组件进行了重新渲染时,React 需要重新构建 Fiber 树。这通常是由调用 setStateuseStateuseReducer 等更新状态的方法触发的。

2. 创建新的 Fiber 节点

在组件需要更新时,React 会为该组件创建一个新的 Fiber 节点。这个过程包括:

  • 确定 Fiber 节点的类型: 根据组件类型(函数组件、类组件、原生 DOM 元素等),确定 Fiber 节点的类型。

  • 复制 props: 将组件的新 props 复制到 Fiber 节点中,以便在渲染时使用。

  • 复制状态(对于类组件): 如果是类组件,将组件的新状态复制到 Fiber 节点中,以便在渲染时使用。

  • 处理 context: 处理组件的 context,确定组件所处的 context 环境。

  • 设置其他属性: 设置 Fiber 节点的其他属性,比如 key(用于优化列表渲染)、effectTag(用于标记更新操作类型)、return(指向父级 Fiber 节点)等。

3. 构建 Fiber 树

在构建过程中,React Fiber 会根据组件树的结构,递归地创建 Fiber 节点,形成一颗 Fiber 树。Fiber 树的结构和 React 元素树非常类似,但是它包含了更多的信息,用于在更新和渲染过程中进行更精细的控制。

  • 确定父子关系: 在构建 Fiber 树的过程中,确定父子组件之间的关系,将子组件的 Fiber 节点设置为父组件的 child 属性。

  • 处理兄弟关系: 在同一个父级下,确定兄弟组件之间的关系,将兄弟组件的 Fiber 节点设置为前一个兄弟组件的 sibling 属性。

4. 备份当前 Fiber 树

在构建新的 Fiber 树之前,需要将当前的 Fiber 树备份起来,以便在更新过程中进行对比,找出需要更新的部分。React 使用双缓冲技术,在两颗 Fiber 树之间切换,一个用于渲染,一个用于协调。

5. 标记待处理的任务

在构建完新的 Fiber 树后,React 会标记需要处理的任务。这些任务通常包括需要执行渲染、副作用(如状态更新、DOM 操作等)的组件。

协调过程

React Fiber 的协调过程是指在组件状态或属性发生变化时,React 决定如何更新组件树的过程。Fiber 的协调过程是一个深度优先、同步的过程,通过协调,React 会找出需要更新的组件,然后将这些组件标记为“脏”(dirty),表示它们在当前更新周期需要进行重新渲染。以下是 React Fiber 的协调过程的详细说明:

1. 触发更新

协调过程通常由外部触发,比如组件的 setState 方法被调用,或者父组件重新渲染时,会触发子组件的协调过程。React 会在合适的时机启动协调过程,例如在 React 事件处理函数执行完成之后。

2. 构建 Update 对象

当组件触发更新时,React 会创建一个 Update 对象,这个对象描述了需要进行的更新操作。Update 对象包含了要更新的组件实例、新的 props、新的状态等信息。

3. 找出需要更新的组件

React 使用一种叫做 Fiber Reconciliation(协调)的算法来找出需要更新的组件。这个算法通过比较新的 React 元素树和之前的 Fiber 树,找出需要更新的部分。在这个比较过程中,React 会识别出哪些组件需要被标记为“脏”,以便在下一步重新渲染。

4. 标记组件为“脏”

当找出需要更新的组件后,React 会将这些组件的 Fiber 节点标记为“脏”(dirty)。这个标记表明这些组件在当前更新周期需要进行重新渲染。标记为“脏”并不意味着立即进行渲染,而是等到整个协调过程完成后,React 会根据“脏”组件的状态来决定是否进行实际的渲染。

5. 构建副作用链表

在协调过程中,React 会记录所有的更新操作,比如插入、删除、更新等,将这些操作记录在一个副作用链表中。这个链表描述了在当前更新周期需要执行的操作。

6. 提交阶段

协调过程完成后,React Fiber 会进入提交阶段。在提交阶段,React 会遍历副作用链表,执行其中的操作。这些操作会直接影响到实际的 DOM 结构,完成组件的更新。这个阶段是同步的,会在一帧的时间片内完成。

双缓冲技术

双缓冲技术(Double Buffering)是一种图形渲染技术,通常用于避免在屏幕上显示图像时出现闪烁的现象。在双缓冲技术中,有两块缓冲区(也称为前缓冲区和后缓冲区)来交替使用,以确保在屏幕上显示图像时的平滑过渡,特别在动画或实时渲染的场景中非常有用。

1. 前缓冲区和后缓冲区

  • 前缓冲区(Front Buffer): 这是显示器上当前可见的图像。用户可以看到的内容实际上是前缓冲区的内容。

  • 后缓冲区(Back Buffer): 这是在内存中准备好的新图像。所有的渲染操作都先在后缓冲区进行,然后再将后缓冲区的内容交换到前缓冲区,使其显示在屏幕上。

2. 绘制过程

  1. 绘制操作在后缓冲区进行: 所有的绘制和渲染操作(如绘制图形、文本、动画等)都在后缓冲区进行,这样用户在前缓冲区看到的内容保持稳定,不会受到渲染过程的影响。

  2. 交换缓冲区: 当所有的绘制操作都完成时,后缓冲区的内容会被与前缓冲区的内容进行交换。这个交换过程是非常快速的,几乎不会引起视觉上的闪烁。

3. 避免闪烁

在单缓冲区绘制中,如果在绘制过程中用户正在观看,可能会看到绘制的中间状态,这就是闪烁。而双缓冲技术通过在后台绘制,然后一次性地切换到前台,避免了这种中间状态的显示,从而避免了闪烁现象。

4. 在计算机图形学和游戏开发中的应用

  • 游戏开发: 在游戏开发中,双缓冲技术确保了游戏画面的平滑刷新,防止了游戏画面在快速刷新时产生闪烁或撕裂的问题。

  • 计算机图形学: 在计算机图形学中,双缓冲技术常用于图形绘制、动画制作等领域,以提供更好的用户体验。

总的来说,双缓冲技术通过平滑地刷新屏幕上的图像,提高了图形界面和游戏的视觉表现。在现代计算机图形学和游戏开发中,双缓冲技术是一项基本而重要的技术。

双缓冲区在React Fiber 中的应用

在 React Fiber 中,双缓冲技术是一种用于优化渲染和协调过程的机制。它确保了在更新组件时,用户界面不会出现不稳定的状态,即防止了用户看到不完整或中间状态的组件。React Fiber 中的双缓冲技术是通过 Fiber 树的备份和切换来实现的。

1. Fiber 树的备份

在 React Fiber 中,有两颗 Fiber 树,分别称为”current fiber tree”和”work-in-progress fiber tree”(通常简称为”current tree”和”work-in-progress tree”)。当前用户界面上显示的内容对应着”current tree”。当需要进行组件更新时,React 不会直接在”current tree”上进行操作,而是在”work-in-progress tree”上进行。

2. 更新过程中的双缓冲切换

在 React 中,组件的更新通常包括两个阶段:协调阶段和渲染阶段。在协调阶段,React Fiber 会比较新的 React 元素树和之前的 Fiber 树(”current tree”)找出需要更新的部分,这个比较是在”work-in-progress tree”上进行的。协调阶段结束后,React Fiber 将”work-in-progress tree” 中更新的部分(被标记为“脏”的部分)记录在一个副作用链表中。

在渲染阶段,React Fiber 将副作用链表中的更新操作应用到真实的 DOM 上。在这个阶段,React Fiber 将”current tree”(用户当前看到的界面)和”work-in-progress tree”(更新后的界面)进行切换,这就是双缓冲技术的体现。切换后,用户将看到更新后的界面,而在此过程中不会看到不稳定的中间状态。

3. 防止用户看到不完整状态

使用双缓冲技术,React 能够保证在更新过程中用户不会看到不完整或者不稳定的界面状态。无论在协调阶段还是渲染阶段,React Fiber 都是在”work-in-progress tree”上进行操作,这样就避免了在用户界面上出现不完整的中间状态。只有在更新完成后,React Fiber 才会将”work-in-progress tree”切换为”current tree”,使得用户看到的是一个完整、稳定的界面。

这种双缓冲技术的应用,使得 React 能够提供更加平滑和可靠的用户界面体验,特别在高度动态、需要频繁更新的应用中,它起到了非常关键的作用。

requestIdleCallback 在React Fiber中的应用

requestIdleCallback 是一个浏览器提供的API,用于在浏览器的空闲时间执行任务。在React Fiber中,requestIdleCallback 被用来更好地利用浏览器的空闲时间,从而提高应用的性能和响应速度。

在React Fiber中,requestIdleCallback 主要用于以下几个方面:

1. 异步任务处理

React Fiber 使用 requestIdleCallback 来处理异步任务,例如在渲染、更新或布局之外执行的任务。这些任务被安排在浏览器空闲时间内执行,以避免阻塞主线程,保持应用的响应性。

2. 任务拆分和优先级处理

React Fiber 将任务拆分成多个小单元(Fiber节点),每个小单元都有自己的优先级。requestIdleCallback 被用来在空闲时间内执行这些小单元,React Fiber 会根据任务的优先级选择性地执行这些小任务,从而优化任务的执行顺序,提高性能。

3. 延迟任务的处理

有些任务不是立即需要执行的,可以稍微等待一下。React Fiber 利用 requestIdleCallback 来安排这些延迟任务,以便在浏览器的空闲时间内执行,而不会影响到用户交互或者页面渲染的性能。

4. 资源加载的优化

在页面加载过程中,有些资源(比如图片、字体等)的加载并不需要立即完成,可以延迟到浏览器空闲时再进行。requestIdleCallback 被用来处理这些资源的延迟加载,以提高页面的加载速度和用户体验。

通过利用 requestIdleCallback,React Fiber 能够更智能地安排任务的执行时间,充分利用浏览器的空闲时间,从而提高应用的性能和用户体验。这种机制使得React应用在忙碌的任务处理同时仍能够响应用户输入,避免了页面卡顿或者阻塞的情况。