Published on

React Fiber 下

React Fiber 架构可以分为三层:

  • Scheduler(调度器)—— 调度任务的优先级,高优任务优先进入 Reconciler

  • Reconciler(协调器)—— 负责找出变化的组件

  • Renderer(渲染器)—— 负责将变化的组件渲染到页面上

如何构建 Fiber 树

使用 performUnitOfWork 方法来创建 Fiber tree

rootFiber 开始进入"递"阶段, 向下深度优先遍历。为遍历到的每个 Fiber 节点调用 beginWork 方法

会根据传入的 Fiber 节点创建子 Fiber 节点,并将这两个 Fiber 节点连接起来。

  • return 表示 父节点
  • child 表示 子节点
  • sibling 表示 下一个兄弟节点

当遍历到 没有子节点时,会进入 "归" 阶段

当某个 Fiber 节点执行完 completeWork,如果其存在兄弟 Fiber 节点(即 Fiber.sibling !== null),会进入其兄弟 Fiber 的 "递" 阶段。

最终会回到 rootFiber, render 结束

更新

onClick = 每一项 * 2

<li>1</li>
<li>2</li>
<li>3</li>
  1. 调度器接收到需要更新的任务,并根据任务的优先级判断是否要中断当前任务,转而处理更高优先级的任务,如果没有则交给 协调器处理

  2. 协调器接收到任务,并在内部构建 Fiber 树以及进行 diff 算法等计算,确定哪些节点需要更新或被删除等操作,协调器还会将这些操作分配给 Fiber 节点,并且打上相应的标记,比如 Update、Placement、Deletion 等标记,如果没有其他变化,交给 渲染器

  3. 接收到打了标记的 Fiber 节点,并根据不同的标记类型做出相应的处理,完成组件的更新、挂载或卸载等操作。如果在处理这些节点的过程中发现时间已经过期,就会将任务重新提交到调度器进行处理

其中 1、2 步骤可以随时被打断:

  • 有优先级更高的任务出现
  • 当前帧没有剩余时间

此外,还通过双缓存的技术:

在内存中创建两颗虚拟 DOM 树:current treeworkInProgress treecurrent tree 是当前正在显示在屏幕上的树,而 workInProgress tree 则是进行更新过程的新树。

在一次更新过程中,React 首先从 current tree 克隆出一个 workInProgress tree,并在工作期间将所有的更改记录在 workInProgress tree 上。一旦更新完成,React 就会将 workInProgress tree 作为新的 current tree,用于下一次更新。

这种双缓存的技术可以避免在渲染期间出现闪烁或卡顿等问题

由于是首屏渲染,页面中还没有挂载任何 DOM,

mount 时

首屏渲染阶段,Fiber tree 从无到有,所有的 Fiber node 都是第一次 mount,产生的副作用包括:

  • 所有 dom 节点的新增;
  • Effect List 的 callback 函数的触发;
  • ref 引用的初始化;

在 commit 阶段,所有的副作用被处理,新增的 dom 节点添加到页面上,componentDidMountuseEffect 的 callback 函数触发。

update 时

新旧 Fiber 做 diff,从而找出需要更新的部分并进行更新。

对已有的 Fiber 树进行更新。React 会对每个需要更新的组件节点创建一个新的 Fiber 节点,并在之前的 Fiber 节点上打上 Update 标记。

React 会使用依赖项数组中的值与上一次渲染时的值进行比较,判断是否需要重新执行对应的 Effect Hook 函数。

useLayoutEffect 它与 useEffect 函数类似,都可以用于处理组件的副作用,但是它会在浏览器渲染前同步执行,从而确保能够访问到最新的 DOM 元素。因此,如果需要在更新后立即执行某些操作或者直接读取 DOM 尺寸等信息时,可以使用 useLayoutEffect 函数。

渲染流程

render 阶段

从根节点开始遍历整个组件树,生成新的 Fiber 树的过程。在 Render 阶段中,React 会执行组件的函数式代码,计算出新的状态和属性,并创建对应的 Fiber 节点。同时,React 还会检查每个 Fiber 节点的依赖项和优先级,确定它们是否需要重新渲染和更新。

commit 阶段

新的 Fiber 树提交到 DOM 树上的过程。在 Commit 阶段中,React 会遍历要更新的 Fiber 节点,并根据需要将对应的变更同步到 DOM 树上。具体来说,React 会执行一系列 DOM 操作,比如插入、删除、更新元素等,从而实现组件的更新和渲染。

commit 阶段的主要工作(即 Renderer 的工作流程)分为三部分:

  • before mutation 阶段(执行 DOM 操作前)

    1. 处理 DOM 节点渲染/删除后的 autoFocus、blur 逻辑

    2. 调用 getSnapshotBeforeUpdate 生命周期钩子

    从 Reactv16 开始,componentWillXXX 钩子前增加了 UNSAFE_前缀。

    究其原因,是因为 Stack Reconciler 重构为 Fiber Reconciler 后,render 阶段的任务可能中断/重新开始,对应的组件在 render 阶段的生命周期钩子(即 componentWillXXX)可能触发多次。

    React 提供了替代的生命周期钩 getSnapshotBeforeUpdate

    getSnapshotBeforeUpdate 是在 commit 阶段内的 before mutation 阶段调用的,由于 commit 阶段是同步的

  1. 调度 useEffect
  • mutation 阶段(执行 DOM 操作) mutation 阶段也是遍历 effectList,执行函数

  • layout 阶段(执行 DOM 操作后) 根据 effectTag 调用不同的处理函数处理 Fiber 并更新 ref