
Fiber 是 React 18 中最小的工作单元,它是一个 JavaScript 对象,通过链表结构(child、sibling、return)描述组件树,使得更新工作可被拆分和中断。
type FiberNode = {
// 核心标识
tag: 'host_root' | 'host_component' | 'function_component' | 'host_text';
key: string | null;
type: string | Function; // 'div' 或组件函数
// 状态与 props
pendingProps: any;
memoizedProps: any;
memoizedState: any; // 对于 FC,存储 Hook 链表
// 链表结构
return: FiberNode | null;
child: FiberNode | null;
sibling: FiberNode | null;
index: number;
// 双缓冲与副作用
alternate: FiberNode | null; // 指向另一棵树的对应节点
flags: 'update' | 'delete' | 'placement' | 'layout_effect' | null;
deletions: FiberNode[] | null;
// 调度相关(React 18 核心)
lanes: number; // 优先级位掩码
childLanes: number;
};每个 Fiber 节点通过 alternate 指针连接 current 树和 workInProgress 树,实现双缓冲渲染。
React 18 使用 MessageChannel 实现宏任务调度,在浏览器空闲时执行工作单元。以下实现 workLoop,它会在渲染超过 5ms 时主动让出主线程:
const DEADLINE = 5; // 毫秒
const channel = new MessageChannel();
let pendingCallback: (() => void) | null = null;
channel.port2.onmessage = () => {
const callback = pendingCallback;
pendingCallback = null;
if (callback) callback();
};
function scheduleCallback(callback: () => void) {
pendingCallback = callback;
channel.port1.postMessage(null);
}
let nextUnitOfWork: FiberNode | null = null;
let workInProgressRoot: FiberNode | null = null;
function performUnitOfWork(fiber: FiberNode): FiberNode | null {
// 1. 协调(reconcile)子节点
reconcileChildren(fiber);
// 2. 如果有子节点,返回子节点继续工作
if (fiber.child) return fiber.child;
// 3. 否则找兄弟节点,或向上回溯
let next: FiberNode | null = fiber;
while (next) {
if (next.sibling) return next.sibling;
next = next.return;
}
return null;
}
function workLoop(deadline: number) {
let shouldYield = false;
while (nextUnitOfWork && !shouldYield) {
nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
shouldYield = performance.now() >= deadline;
}
if (nextUnitOfWork) {
scheduleCallback(() => workLoop(performance.now() + DEADLINE));
} else {
// 所有工作完成,提交到 DOM
commitRoot(workInProgressRoot!);
}
}
export function render(element: JSX.Element, container: HTMLElement) {
// 创建 root Fiber
const rootFiber: FiberNode = {
tag: 'host_root',
type: null,
key: null,
pendingProps: element,
memoizedProps: null,
memoizedState: null,
return: null,
child: null,
sibling: null,
index: 0,
alternate: null,
flags: null,
deletions: null,
lanes: 0,
childLanes: 0,
};
workInProgressRoot = rootFiber;
nextUnitOfWork = rootFiber;
scheduleCallback(() => workLoop(performance.now() + DEADLINE));
}协调是 React 的灵魂。我们在 reconcileChildren 中比较新旧 Fiber 子节点,决定是复用、更新还是创建新节点。
function reconcileChildren(workInProgress: FiberNode) {
const children = workInProgress.pendingProps.children;
// 简化版:仅处理单子节点
if (typeof children === 'string' || typeof children === 'number') {
// 文本节点
const newFiber: FiberNode = {
tag: 'host_text',
type: null,
key: null,
pendingProps: children,
memoizedProps: null,
memoizedState: null,
return: workInProgress,
child: null,
sibling: null,
index: 0,
alternate: null,
flags: 'update',
deletions: null,
lanes: 0,
childLanes: 0,
};
workInProgress.child = newFiber;
return;
}
// 处理元素节点
if (typeof children === 'object' && children !== null) {
const newFiber: FiberNode = {
tag: typeof children.type === 'function' ? 'function_component' : 'host_component',
type: children.type,
key: children.key ?? null,
pendingProps: children.props,
memoizedProps: null,
memoizedState: null,
return: workInProgress,
child: null,
sibling: null,
index: 0,
alternate: null,
flags: 'placement',
deletions: null,
lanes: 0,
childLanes: 0,
};
workInProgress.child = newFiber;
}
}React 18 的 Hook 调用顺序依赖 Fiber 节点上的 memoizedState 链表。以下是 useState 的精简实现:
type Hook = {
memoizedState: any;
queue: any[];
next: Hook | null;
};
let currentlyRenderingFiber: FiberNode | null = null;
let hookIndex = 0;
function mountWorkInProgressHook(): Hook {
const hook: Hook = {
memoizedState: null,
queue: [],
next: null,
};
const fiber = currentlyRenderingFiber!;
if (!fiber.memoizedState) {
fiber.memoizedState = hook;
} else {
// 遍历到链表末尾
let current = fiber.memoizedState;
while (current.next) current = current.next;
current.next = hook;
}
return hook;
}
export function useState<T>(initial: T): [T, (action: T | ((prev: T) => T)) => void] {
const fiber = currentlyRenderingFiber!;
const hook = mountWorkInProgressHook();
// 读取 alternate 树中的历史状态
const alternate = fiber.alternate;
let state: T = initial;
if (alternate && alternate.memoizedState) {
// 真实场景需遍历到对应索引的 Hook
state = (alternate.memoizedState as Hook).memoizedState ?? initial;
}
hook.memoizedState = state;
const setState = (action: T | ((prev: T) => T)) => {
const newState = typeof action === 'function' ? (action as (prev: T) => T)(state) : action;
// 触发更新(重新调度渲染)
// 实际实现需要标记 fiber 并触发 workLoop
scheduleUpdate(fiber, newState);
};
return [state, setState];
}
// 在执行函数组件时设置当前 fiber
function renderFunctionComponent(fiber: FiberNode) {
currentlyRenderingFiber = fiber;
hookIndex = 0;
const Component = fiber.type as Function;
const props = fiber.pendingProps;
const children = Component(props);
fiber.memoizedProps = props;
// 继续协调 children...
}完成渲染后,将 workInProgress 树的变化应用到真实 DOM。React 18 支持 useLayoutEffect 和 useEffect 分别在提交前后执行。
function commitRoot(root: FiberNode) {
// 1. 递归遍历 fiber 树,执行 DOM 操作
commitWork(root.child);
// 2. 将 workInProgress 树变为 current 树
root.alternate = root;
// 3. 执行 useEffect(在浏览器绘制后异步执行)
scheduleEffectCallbacks(root);
}
function commitWork(fiber: FiberNode | null) {
if (!fiber) return;
// 找到最近的 host_component 父节点
let parent = fiber.return;
while (parent && parent.tag !== 'host_component') parent = parent.return;
const domParent = (parent as any).domInstance;
if (fiber.tag === 'host_component' || fiber.tag === 'host_text') {
if (fiber.flags === 'placement') {
// 创建 DOM 并插入
const dom = createDOM(fiber);
domParent.appendChild(dom);
} else if (fiber.flags === 'update') {
updateDOM(fiber);
}
}
commitWork(fiber.child);
commitWork(fiber.sibling);
}我们仅用数百行代码便重现了 React 18 的核心架构:Fiber 链表使渲染可中断,MessageChannel + 时间切片实现并发调度,双缓冲树与alternate指针保障状态一致性,Hooks通过链表顺序关联状态。
这个微型实现虽不包含事件合成、Suspense、并发优先级调度等完整功能,但它揭示了 React 18 最本质的设计哲学。建议读者运行代码,在浏览器 DevTools 的 Performance 面板中观察 workLoop 的切片效果,你会真正理解 React 如何做到“渲染不阻塞用户输入”。
React 18 的源码虽庞大,但核心思想高度凝练。掌握 Fiber 架构,你就掌握了现代前端框架设计的精髓。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。