React 的 Fiber 架构 是在 React 16 中引入的重大重构,其核心目的是支持异步渲染。在此之前,React 使用的是栈协调器(Stack Reconciler),它采用递归方式处理组件树的更新,无法中断或优先级调度。
在 Fiber 架构中,双缓冲(Double Buffering)技术 被引入用于管理 current
和 workInProgress
树之间的切换。这一机制允许 React 在执行更新的过程中保留当前状态,并在完成更新后一次性将结果提交到 DOM。
ReactFiber.old.js
ReactFiberWorkLoop.old.js
这两个文件是 React 内部协调器的核心部分,其中定义了 Fiber
对象结构和工作循环逻辑。
React 使用两棵 Fiber 树:
current
: 表示当前屏幕上渲染的 UI。workInProgress
: 表示正在构建的新版本 UI。当一次更新开始时,React 会基于 current
创建 workInProgress
树,所有的更新操作都在这棵树上进行。一旦更新完成并通过 commitRoot()
提交,workInProgress
将成为新的 current
。
Update Request
↓
Create workInProgress from current
↓
Perform Work (beginWork / completeWork)
↓
Commit workInProgress to DOM
↓
Swap current and workInProgress
workInProgress
/**
* 创建或复用工作进中的 Fiber 节点(WorkInProgress)
* @param current 当前 Fiber 节点(可能是主机节点或组件节点)
* @param pendingProps 新的 props
* @returns 返回工作进中的 Fiber 节点
*/
function createWorkInProgress(current: Fiber, pendingProps: any): Fiber {
// 尝试获取当前 Fiber 的 alternate(即工作进中的 Fiber)
let workInProgress = current.alternate;
// 如果 alternate 不存在(说明是初次创建)
if (workInProgress === null) {
// 创建一个新的 Fiber 节点作为工作进中的节点
workInProgress = createFiber(
current.tag, // 复用当前 Fiber 的类型(如函数组件、类组件、主机节点等)
pendingProps, // 设置新的 props
current.mode // 复用当前 Fiber 的模式(如严格模式、并发模式等)
);
// 复制必要的属性到新的工作进中节点
workInProgress.elementType = current.elementType; // 组件类型(如函数、类、字符串等)
workInProgress.key = current.key; // 子节点的唯一标识
// 设置双向链表关系
workInProgress.alternate = current; // 新节点的 alternate 指向当前节点
current.alternate = workInProgress; // 当前节点的 alternate 指向新节点
} else {
// 如果 alternate 已存在(说明是复用)
// 只需要更新 props 和重置 effectTag
workInProgress.pendingProps = pendingProps; // 更新为新的 props
workInProgress.effectTag = NoEffect; // 重置副作用标记为无效果
}
// 返回工作进中的 Fiber 节点
return workInProgress;
}
关键点说明:
- 这个函数是 React Fiber 架构中协调(reconciliation)过程的核心部分
- 它实现了 Fiber 节点的双缓存机制(current ↔ workInProgress)
- 初次渲染时会创建新的 alternate 节点,更新时则复用已有的 alternate 节点
- 通过维护双向链表(alternate 指针)来实现快速切换
- 每次更新都会重置 effectTag 为 NoEffect,后续会根据实际情况设置新的标记
workInProgress
树(beginWork)遍历整个组件树,调用 beginWork()
方法,根据组件类型(如 FunctionComponent、ClassComponent)决定是否需要重新渲染。
在完成阶段,构建最终的 DOM 结构并标记副作用(如插入、更新、删除等)。
将 workInProgress
树提交到真实 DOM,并更新 current
指针。
function commitRoot(root) {
const finishedWork = root.finishedWork;
root.current = finishedWork; // 切换 current 指针
commitMutationEffects(finishedWork, root); // 执行 DOM 更新
}
以下是一个简化版的双缓冲机制模拟代码:
// 模拟 Fiber 对象结构
class Fiber {
constructor(tag, key, elementType, pendingProps, mode) {
this.tag = tag; // 节点类型标识(如 HostRoot, FunctionComponent, HostComponent 等)
this.key = key; // 用于协调(reconciliation)的唯一标识
this.elementType = elementType; // 组件类型(如函数、类、DOM 标签等)
this.pendingProps = pendingProps; // 待处理的 props
this.mode = mode; // 渲染模式(如 ConcurrentMode, BlockingMode 等)
// 链表指针
this.alternate = null; // 指向另一棵树的对应节点(current ↔ workInProgress)
this.child = null; // 指向第一个子节点
this.sibling = null; // 指向下一个兄弟节点
this.return = null; // 指向父节点
// 实例相关
this.stateNode = null; // 关联的实例(DOM 节点或组件实例)
this.memoizedState = null; // 记忆化的状态(用于函数组件)
this.updateQueue = null; // 更新队列(存储未处理的更新)
// 副作用相关
this.effectTag = 'NoEffect'; // 副作用标记(如 Placement, Update, Deletion 等)
}
}
// 初始化当前 Fiber 树(模拟根节点)
let currentFiber = new Fiber('HostRoot', null, null, null, 'ConcurrentMode');
let workInProgressFiber = null; // 工作进中的 Fiber 树
/**
* 从当前 Fiber 创建或复用 workInProgress Fiber
* @param {Fiber} current 当前 Fiber 节点
* @returns {Fiber} workInProgress Fiber 节点
*/
function createWorkInProgressFromCurrent(current) {
if (!current.alternate) {
// 如果 alternate 不存在,创建新的 workInProgress 节点
workInProgressFiber = new Fiber(
current.tag,
current.key,
current.elementType,
current.pendingProps,
current.mode
);
// 建立双向链表关系
workInProgressFiber.alternate = current;
current.alternate = workInProgressFiber;
} else {
// 如果 alternate 已存在,复用已有的节点
workInProgressFiber = current.alternate;
// 更新 props 和重置副作用
workInProgressFiber.pendingProps = current.pendingProps;
workInProgressFiber.effectTag = 'NoEffect';
}
return workInProgressFiber;
}
/**
* 模拟 beginWork 阶段(开始处理 Fiber 节点)
* @param {Fiber} current 当前 Fiber 节点
* @param {Fiber} workInProgress 工作进中的 Fiber 节点
* @returns {Fiber|null} 返回下一个要处理的子节点或 null
*/
function beginWork(current, workInProgress) {
console.log('Begin work on:', workInProgress.key || 'root');
// 这里可以加入 diff 算法判断是否需要更新
// 模拟返回第一个子节点(实际实现会更复杂)
return workInProgress.child;
}
/**
* 模拟 completeWork 阶段(完成处理 Fiber 节点)
* @param {Fiber} workInProgress 工作进中的 Fiber 节点
*/
function completeWork(workInProgress) {
console.log('Complete work on:', workInProgress.key || 'root');
// 模拟构建 DOM 或设置副作用标记
workInProgress.effectTag = 'Update';
}
/**
* 模拟 commit 阶段(提交变更到 DOM)
*/
function commitRoot() {
console.log('Committing root...');
// 切换当前树为 workInProgress 树
currentFiber = workInProgressFiber;
// 清空 workInProgress 树
workInProgressFiber = null;
}
/**
* 处理单个工作单元
* @param {Fiber} unitOfWork 要处理的 Fiber 节点
* @returns {Fiber|null} 返回下一个要处理的节点或 null
*/
function performUnitOfWork(unitOfWork) {
const current = unitOfWork.alternate;
// 开始处理当前节点
const next = beginWork(current, unitOfWork);
if (next === null) {
// 如果没有子节点,完成当前节点的处理
completeWork(unitOfWork);
}
return next;
}
/**
* 工作循环(模拟 Fiber 架构的协调过程)
* @param {boolean} isYieldy 是否可中断(模拟并发模式)
*/
function workLoop(isYieldy) {
let unitOfWork = workInProgressFiber;
while (unitOfWork !== null) {
unitOfWork = performUnitOfWork(unitOfWork);
}
// 如果不可中断且工作完成,提交变更
if (!isYieldy && unitOfWork === null) {
commitRoot();
}
}
/**
* 启动更新流程
*/
function scheduleUpdate() {
// 创建 workInProgress 树
workInProgressFiber = createWorkInProgressFromCurrent(currentFiber);
// 开始工作循环
workLoop(false);
}
// 触发初始更新
scheduleUpdate();
React 的双缓冲机制融合了多种设计模式:
设计模式 | 应用场景 |
---|---|
享元模式 | alternate 字段复用已有 Fiber 节点,避免频繁创建对象 |
职责链模式 | beginWork -> completeWork -> commitRoot 形成一条完整的更新链条 |
命令模式 | effectTag 表示要执行的 DOM 操作(Insert、Update、Delete) |
观察者模式 | 通过 updateQueue 和 pendingProps 实现组件更新通知 |
React 中的 current 和 workInProgress 是什么?它们之间如何切换?
current
表示当前渲染的 Fiber 树;workInProgress
是正在构建的新树。更新完成后通过 commitRoot()
切换。为什么 React 引入双缓冲机制?解决了什么问题?
Fiber 节点中的 alternate 字段有什么作用?
React 如何创建和复用 workInProgress 树?
createFiber()
,后续更新时复用 alternate
。beginWork 和 completeWork 分别负责什么任务?
beginWork
决定是否需要更新子节点;completeWork
收集副作用并准备提交。effectTag 的作用是什么?有哪些常见值?
Update
, Placement
, Deletion
。React 是如何保证更新过程中 UI 不闪烁的?
workInProgress
上完成后再统一提交,避免中间状态暴露给用户。双缓冲机制对性能有何影响?是否存在内存开销?
workInProgress 是否总是从 current 克隆而来?有没有例外?
current
创建 workInProgress
,除非首次挂载。在并发模式下,双缓冲机制如何应对多次更新?
expirationTime
和 priority
控制更新顺序,高优先级更新可中断低优先级更新。React 的双缓冲机制是其异步渲染能力的核心支撑之一。通过 current
和 workInProgress
两棵树的协作,React 实现了高效、安全的状态更新和 DOM 渲染。理解这一机制有助于深入掌握 React 的内部运行原理,也为优化应用性能提供了理论基础。