React Fiber 架构
理解 Fiber 的链表结构、时间切片和可中断渲染机制
问题
解释 React Fiber 架构的设计原理,包括时间切片、链表结构和可中断渲染。
解答
为什么需要 Fiber
React 15 使用递归遍历虚拟 DOM 树(Stack Reconciler),一旦开始就无法中断,长时间占用主线程会导致页面卡顿。
// React 15 的递归协调(不可中断)
function reconcile(element, container) {
// 递归处理所有子节点,无法中断
element.children.forEach(child => {
reconcile(child, container);
});
}
Fiber 节点结构
Fiber 将树结构改为链表,每个节点通过三个指针连接:
// Fiber 节点结构
const fiber = {
// 节点类型信息
type: 'div',
key: null,
// 链表指针
return: parentFiber, // 父节点
child: firstChildFiber, // 第一个子节点
sibling: nextFiber, // 下一个兄弟节点
// 状态相关
stateNode: domElement, // 对应的真实 DOM
pendingProps: {}, // 新的 props
memoizedProps: {}, // 上次渲染的 props
memoizedState: {}, // 上次渲染的 state
// 副作用
flags: NoFlags, // 标记需要执行的操作
nextEffect: null, // 下一个有副作用的节点
};
链表遍历方式
// Fiber 树的遍历顺序:深度优先
function performUnitOfWork(fiber) {
// 1. 处理当前节点
beginWork(fiber);
// 2. 如果有子节点,返回子节点
if (fiber.child) {
return fiber.child;
}
// 3. 没有子节点,找兄弟节点或回溯到父节点
let nextFiber = fiber;
while (nextFiber) {
// 完成当前节点的工作
completeWork(nextFiber);
// 有兄弟节点就处理兄弟
if (nextFiber.sibling) {
return nextFiber.sibling;
}
// 没有兄弟就回到父节点
nextFiber = nextFiber.return;
}
return null;
}
时间切片与可中断渲染
// 工作循环(简化版)
let workInProgress = null;
let shouldYield = false;
function workLoop(deadline) {
// 有剩余时间且有待处理的工作
while (workInProgress && !shouldYield) {
// 执行一个工作单元
workInProgress = performUnitOfWork(workInProgress);
// 检查是否需要让出控制权(时间切片)
shouldYield = deadline.timeRemaining() < 1;
}
// 还有工作未完成,请求下一次调度
if (workInProgress) {
requestIdleCallback(workLoop);
} else {
// 所有工作完成,提交更新
commitRoot();
}
}
// 启动调度
requestIdleCallback(workLoop);
双缓冲机制
// React 维护两棵 Fiber 树
const root = {
current: currentFiber, // 当前显示的树
workInProgress: wipFiber, // 正在构建的树
};
// 提交阶段:交换两棵树
function commitRoot() {
// 执行 DOM 操作
commitWork(root.workInProgress);
// 交换指针
root.current = root.workInProgress;
root.workInProgress = null;
}
优先级调度
// 不同更新有不同优先级
const ImmediatePriority = 1; // 同步,最高优先级
const UserBlockingPriority = 2; // 用户交互
const NormalPriority = 3; // 普通更新
const LowPriority = 4; // 低优先级
const IdlePriority = 5; // 空闲时执行
// 高优先级可以打断低优先级
function ensureRootIsScheduled(root) {
const nextLanes = getNextLanes(root);
const existingCallbackPriority = root.callbackPriority;
// 新任务优先级更高,取消旧任务
if (newCallbackPriority > existingCallbackPriority) {
cancelCallback(existingCallback);
}
// 调度新任务
scheduleCallback(newCallbackPriority, performConcurrentWorkOnRoot);
}
关键点
- 链表结构:通过 child、sibling、return 三个指针将树转为链表,支持随时中断和恢复
- 时间切片:每个 Fiber 节点是一个工作单元,处理完一个就检查剩余时间,没时间就让出主线程
- 双缓冲:维护 current 和 workInProgress 两棵树,构建完成后直接切换,避免中间状态
- 优先级调度:不同更新有不同优先级,高优先级任务可以打断低优先级任务
- 两个阶段:render 阶段可中断(构建 Fiber 树),commit 阶段不可中断(操作 DOM)
目录