在 React Fiber 架构里,协调(Reconcile)阶段处于虚拟 DOM(VDOM)与实际 DOM 渲染之间,主要承担着把 VDOM 转化为 Fiber 节点树、开展 Diff 比较并标记节点变化的任务。下面详细阐述协调阶段的具体工作流程:
1. 初始化
- Fiber 树创建:在协调阶段启动时,React 会依据当前的 VDOM 构建一个新的 Fiber 树。每个 VDOM 节点都会被转化成一个对应的 Fiber 节点,这些 Fiber 节点相互连接形成树状结构。
- 根 Fiber 节点:整个 Fiber 树的起始点是根 Fiber 节点,它代表着整个 React 应用的根组件。从根 Fiber 节点开始,React 会递归地遍历 VDOM 树,为每个节点创建对应的 Fiber 节点。
2. 深度优先遍历
- 遍历策略:React 采用深度优先遍历的方式来处理 Fiber 树。从根 Fiber 节点开始,沿着树的深度方向依次处理每个 Fiber 节点。
- 工作循环:在遍历过程中,React 借助工作循环(Work Loop)逐个处理 Fiber 节点。工作循环会持续执行,直至所有 Fiber 节点都被处理完毕。不过,工作循环是可中断的,这意味着 React 能够在执行过程中暂停当前的协调任务,去处理更为紧急的任务,处理完后再恢复之前的协调任务。
3. Diff 算法执行
- 比较新旧节点:在处理每个 Fiber 节点时,React 会运用 Diff 算法来比较新旧 VDOM 节点的差异。Diff 算法的核心目标是尽可能高效地找出需要更新、添加或删除的节点。
- 三种主要比较情况
- 同一类型的元素 :若新旧节点属于同一类型的元素(如都是
<div>
标签),React 会保留该节点的 DOM 节点,仅更新其属性。例如,当<div>
标签的className
属性发生变化时,React 只会更新该属性,而不会重新创建<div>
节点。 - 不同类型的元素 :要是新旧节点属于不同类型的元素(如从
<div>
变为<p>
),React 会销毁旧的 DOM 节点,然后创建并插入新的 DOM 节点。 - 列表节点 :对于列表节点,React 会使用
key
属性来优化 Diff 过程。通过key
,React 可以更准确地识别哪些元素被添加、删除或移动,从而减少不必要的 DOM 操作。
- 同一类型的元素 :若新旧节点属于同一类型的元素(如都是
4. 标记 effectTag
- 标记节点变化 :在 Diff 过程中,React 会为每个需要更新、添加或删除的 Fiber 节点标记相应的
effectTag
。effectTag
是一个枚举值,用于表示节点的变化类型,常见的effectTag
包括:Placement
:表示该节点需要被插入到 DOM 中。Update
:表示该节点需要更新其属性或内容。Deletion
:表示该节点需要从 DOM 中删除。
- 副作用链表 :所有标记了
effectTag
的 Fiber 节点会被添加到一个副作用链表(Effect List)中,这个链表会在后续的提交(Commit)阶段被处理。
5. 保存状态和副作用
- 状态保存:在协调阶段,React 会保存每个 Fiber 节点的状态信息,包括组件的状态、属性等。这些状态信息会在后续的渲染和更新过程中被使用。
- 副作用记录 :对于使用了
useEffect
等 Hook 的组件,React 会记录其副作用函数。这些副作用函数会在提交阶段的不同时机执行。
6. 协调阶段结束
- 生成新的 Fiber 树 :当工作循环完成对所有 Fiber 节点的处理后,协调阶段结束,此时会生成一棵新的 Fiber 树,其中的每个节点都标记了相应的
effectTag
。 - 进入提交阶段 :新的 Fiber 树会被传递给提交(Commit)阶段,在提交阶段,React 会根据
effectTag
一次性更新实际的 DOM 节点,从而完成页面的更新。
综上所述,协调阶段在 React Fiber 架构中起着关键作用,它通过 Diff 算法和 effectTag
标记,高效地找出 VDOM 的差异,并为后续的 DOM 更新做好准备。同时,协调阶段的可中断特性使得 React 能够更好地处理高优先级任务,提升页面的响应性能。