React Fiber 树遍历机制
React Fiber 树的遍历是一个基于深度优先的循环遍历过程。它与 React 15 及之前版本依赖递归调用栈不同,Fiber 架构的核心改进是将遍历过程拆解为可中断的工作单元。
整个遍历过程主要发生在render 阶段。下面我们来拆解这个过程。
1. Fiber 数据结构:遍历的基础
Fiber 树之所以能被循环遍历,关键在于其独特的链表式数据结构。每个 Fiber 节点通过三个核心指针来连接:
child: 指向第一个子节点。sibling: 指向下一个兄弟节点。return: 指向父节点,用于工作完成后的回溯。
这种结构将一棵树转换为了一个可通过指针遍历的链表。
2. 遍历执行流程:workLoop 与 performUnitOfWork
整个遍历过程由一个名为 workLoop 的循环驱动。
// 同步模式下的工作循环
function workLoopSync() {
while (workInProgress !== null) {
performUnitOfWork(workInProgress);
}
}
workInProgress 是一个全局指针,指向下一个需要被处理的 Fiber 节点。performUnitOfWork 函数是遍历的核心,它处理当前节点,并返回下一个要处理的节点。
遍历的两大阶段:beginWork 与 completeWork
performUnitOfWork 对每个节点的处理分为“递”和“归”两个阶段。
- 递(beginWork):当“进入”一个节点时,会执行
beginWork。它的主要任务是创建或复用子 Fiber 节点,并建立起父子间的child和return连接。 - 归(completeWork):当“离开”一个节点时,会执行
completeWork。它主要处理节点自身的副作用(如 DOM 更新)并收集有副作用的节点,形成副作用链表(effectList)。
遍历的顺序逻辑
performUnitOfWork 函数决定了遍历的具体顺序。
- 优先处理子节点:对当前节点执行
beginWork后,如果它有child,则下一个工作单元就是该child节点。这保证了深度优先的遍历顺序。 - 处理兄弟或回溯:如果当前节点没有
child(即叶子节点),则执行completeWork。之后:- 若它有
sibling(兄弟节点),则下一个工作单元是该sibling节点。 - 若没有
sibling,则通过return指针回溯到父节点,并执行父节点的completeWork。
- 若它有
- 循环直至根节点:这个过程不断重复,直到完成根节点
rootFiber的completeWork,此时workInProgress变为null,遍历结束。
3. 可中断与恢复:链表遍历的核心优势
链式遍历让“可中断”成为可能。
在并发模式下,workLoop 会在执行每个工作单元后检查是否还有剩余时间。
// 并发模式下的工作循环
function workLoopConcurrent() {
while (workInProgress !== null && !shouldYield()) {
performUnitOfWork(workInProgress);
}
}
- 中断:如果
shouldYield()返回true(表示时间用尽或有更高优先级任务),循环会暂停。因为workInProgress指针保存了当前处理到的节点,所以可以随时停止。 - 恢复:当下次浏览器有空闲时间时,
workLoop会从上次保存的workInProgress节点继续执行,实现无缝恢复。
总结
React Fiber 树的遍历可以总结为以下几点:
- 数据结构:使用
child、sibling、return指针构建的链表树。 - 遍历方式:深度优先遍历。
- 执行方式:通过
workLoop循环和performUnitOfWork函数驱动,替代了传统的递归。 - 核心流程:每个节点经历
beginWork(递) 和completeWork(归) 两个阶段。 - 关键特性:基于链表的遍历是可中断、可恢复的,这是实现时间切片和并发更新的基础。