React 的 Fiber 架构是 React 16 中引入的重大重构,它彻底改变了 React 的渲染机制,为并发特性(如 Concurrent Mode)奠定了基础。
为什么需要 Fiber 架构?
传统 Stack Reconciler 的局限性
在 React 16 之前,React 使用栈协调器(Stack Reconciler),其存在以下问题:
- 不可中断的递归遍历:渲染过程是同步、不可中断的
- 阻塞主线程:大型组件树会导致界面卡顿
- 无法优先处理高优先级更新:所有更新同等对待
Fiber 架构的解决方案
Fiber 架构引入了:
- 可中断的渲染过程:将工作分解为小单元
- 优先级调度:不同更新有不同的优先级
- 并发渲染能力:为 Concurrent Mode 提供基础
Fiber 节点的核心结构
Fiber 是 React 的最小工作单元,每个组件对应一个 Fiber 节点:
javascript
// Fiber 节点结构(简化版)
type Fiber = {
// 标识信息
tag: WorkTag, // 组件类型(函数组件、类组件、宿主组件等)
key: null | string, // 唯一标识
type: any, // 组件函数/类或DOM标签名
// 树结构信息
return: Fiber | null, // 父节点
child: Fiber | null, // 第一个子节点
sibling: Fiber | null, // 下一个兄弟节点
// 状态信息
pendingProps: any, // 新的 props
memoizedProps: any, // 上一次渲染的 props
memoizedState: any, // 上一次渲染的状态(hooks、state等)
stateNode: any, // 对应的实例(DOM节点、组件实例)
// 副作用相关
flags: Flags, // 需要执行的副作用标记(增、删、更新)
subtreeFlags: Flags, // 子树中的副作用标记
deletions: Fiber[] | null, // 待删除的子节点
// 工作进度相关
alternate: Fiber | null, // 上一次渲染的fiber节点(用于diff)
lanes: Lanes, // 优先级车道
childLanes: Lanes, // 子节点的优先级车道
// Hook 相关(函数组件)
memoizedState: any, // Hook 链表头
};
Fiber 树的双缓存机制
React 使用双缓存技术来避免渲染过程中的视觉闪烁:
- Current Tree:当前屏幕上显示内容对应的 Fiber 树
- WorkInProgress Tree:正在构建的新 Fiber 树
javascript
// 双缓存工作机制
function updateComponent() {
// 从当前fiber创建workInProgress fiber
const current = currentlyRenderingFiber.alternate;
if (current !== null) {
// 复用现有的fiber节点
workInProgress = createWorkInProgress(current, pendingProps);
} else {
// 创建新的fiber节点
workInProgress = createFiberFromTypeAndProps(
// ...参数
);
}
// 处理workInProgress树...
}
完整的渲染流程
React 的渲染过程分为两个主要阶段:
1. Render 阶段(可中断)
Render 阶段是异步、可中断的,负责计算变更:
javascript
// Render 阶段工作循环
function workLoop(deadline) {
let shouldYield = false;
while (nextUnitOfWork !== null && !shouldYield) {
// 执行当前工作单元
nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
// 检查是否需要让出主线程
shouldYield = deadline.timeRemaining() < 1;
}
if (nextUnitOfWork !== null) {
// 还有工作,稍后继续
requestIdleCallback(workLoop);
} else {
// 所有工作完成,进入提交阶段
commitRoot();
}
}
performUnitOfWork 的深度优先遍历
javascript
function performUnitOfWork(fiber) {
// 1. 开始工作:创建子fiber节点(调用组件render方法)
const next = beginWork(fiber);
if (next !== null) {
return next; // 如果有子节点,返回子节点继续处理
}
// 2. 没有子节点,完成当前节点工作,转向兄弟节点或父节点
let current = fiber;
while (current !== null) {
// 完成当前节点(生成effect列表等)
completeWork(current);
if (current.sibling !== null) {
return current.sibling; // 处理兄弟节点
}
current = current.return; // 返回父节点
}
return null; // 遍历完成
}
beginWork:处理组件更新
javascript
function beginWork(fiber) {
switch (fiber.tag) {
case FunctionComponent:
// 处理函数组件
return updateFunctionComponent(fiber);
case ClassComponent:
// 处理类组件
return updateClassComponent(fiber);
case HostComponent:
// 处理DOM元素
return updateHostComponent(fiber);
// ... 其他组件类型
}
}
function updateFunctionComponent(fiber) {
// 准备Hooks环境
prepareToUseHooks(fiber);
// 调用组件函数,获取子元素
const children = fiber.type(fiber.pendingProps);
// 协调子元素
reconcileChildren(fiber, children);
return fiber.child; // 返回第一个子节点
}
completeWork:完成节点处理
javascript
function completeWork(fiber) {
switch (fiber.tag) {
case HostComponent:
// 处理DOM元素的属性更新等
if (fiber.stateNode !== null) {
// 更新现有的DOM节点
updateDOMProperties(fiber.stateNode, fiber.memoizedProps, fiber.pendingProps);
} else {
// 创建新的DOM节点
const instance = createInstance(fiber.type, fiber.pendingProps);
fiber.stateNode = instance;
}
break;
// ... 其他组件类型
}
// 收集effect到父节点
if (fiber.flags !== NoFlags) {
// 将当前fiber的effect添加到父节点的effect列表中
let parent = fiber.return;
while (parent !== null) {
parent.subtreeFlags |= fiber.flags;
parent = parent.return;
}
}
}
2. Commit 阶段(不可中断)
Commit 阶段是同步、不可中断的,负责将变更应用到DOM:
javascript
function commitRoot() {
// 1. 预处理:调用getSnapshotBeforeUpdate等
commitBeforeMutationEffects();
// 2. 应用DOM变更
commitMutationEffects();
// 3. 将workInProgress树切换为current树
root.current = finishedWork;
// 4. 处理布局effect(如useLayoutEffect)
commitLayoutEffects();
// 5. 调度被动effect(useEffect)
schedulePassiveEffects();
}
commitMutationEffects:处理DOM变更
javascript
function commitMutationEffects() {
// 遍历effect列表,执行DOM操作
while (nextEffect !== null) {
const flags = nextEffect.flags;
if (flags & Placement) {
// 插入新节点
commitPlacement(nextEffect);
}
if (flags & Update) {
// 更新节点
commitUpdate(nextEffect);
}
if (flags & Deletion) {
// 删除节点
commitDeletion(nextEffect);
}
nextEffect = nextEffect.nextEffect;
}
}
优先级调度机制
Fiber 架构引入了优先级概念,确保高优先级更新优先处理:
javascript
// 优先级类型(简化)
const SyncLane = 0b0000000000000000000000000000001; // 同步优先级
const InputContinuousLane = 0b0000000000000000000000000000100; // 连续输入
const DefaultLane = 0b0000000000000000000000000010000; // 默认优先级
// 基于优先级的调度
function scheduleUpdateOnFiber(fiber, lane) {
// 标记优先级
fiber.lanes = mergeLanes(fiber.lanes, lane);
// 调度更新
if (lane === SyncLane) {
// 同步更新,立即执行
performSyncWorkOnRoot(root);
} else {
// 异步更新,根据优先级调度
ensureRootIsScheduled(root);
}
}
并发模式下的工作方式
在并发模式下,React 可以中断低优先级工作来处理高优先级更新:
javascript
// 高优先级更新中断低优先级工作
function handleUserInput() {
// 高优先级更新(用户输入)
scheduleUpdateOnFiber(root, InputContinuousLane);
// 如果当前有低优先级渲染正在进行...
// React 会中断它,先处理高优先级更新
}
// 被中断的工作可以稍后重新开始
function resumeInterruptedWork(interruptedFiber) {
// 从中断的地方继续工作
nextUnitOfWork = interruptedFiber;
requestIdleCallback(workLoop);
}
错误处理机制
Fiber 架构改进了错误处理:
javascript
function renderRoot() {
try {
// 正常的渲染工作
workLoop();
} catch (error) {
// 处理错误,寻找错误边界
let fiber = nextUnitOfWork;
while (fiber !== null) {
if (fiber.tag === ClassComponent &&
typeof fiber.type.getDerivedStateFromError === 'function') {
// 找到错误边界组件
captureError(fiber, error);
break;
}
fiber = fiber.return;
}
// 重新尝试渲染
restartRender();
}
}
性能优化特性
1. 增量渲染
Fiber 将渲染工作分解为小单元,可以分段完成:
javascript
// 时间分片示例
function workLoopConcurrent(deadline) {
while (nextUnitOfWork !== null && deadline.timeRemaining() > 0) {
nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
}
if (nextUnitOfWork !== null) {
// 还有工作,稍后继续
requestIdleCallback(workLoopConcurrent);
}
}
2. 子树渲染跳过
当 props 未变化时,可以跳过整个子树的渲染:
javascript
function bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes) {
if (current !== null) {
// 检查props是否变化
const oldProps = current.memoizedProps;
const newProps = workInProgress.pendingProps;
if (oldProps === newProps && !hasContextChanged()) {
// props未变化,可以跳过整个子树
return null;
}
}
// 需要重新渲染
return updateFunctionComponent(workInProgress);
}
总结
React Fiber 架构的核心创新:
- 可中断的异步渲染:将渲染工作分解为小单元,可以暂停和恢复
- 优先级调度:不同更新有不同优先级,确保用户体验流畅
- 双缓存机制:避免渲染过程中的视觉闪烁
- 增量提交:DOM 变更分批进行,减少布局抖动
- 更好的错误处理:完善的错误边界机制