Fiber 是一个链表结构的工作单元,每个 React 元素对应一个 Fiber 节点:
// Fiber 节点核心结构(简化版) function FiberNode(tag, pendingProps, key) { // 实例属性 this.tag = tag; // 组件类型(Function/Class/Host) this.key = key; this.type = null; // 元素类型(div/span/函数组件) this.stateNode = null; // 真实 DOM 或组件实例 // 链表树结构 this.return = null; // 父 Fiber this.child = null; // 子 Fiber this.sibling = null; // 兄弟 Fiber // 工作相关 this.pendingProps = pendingProps; this.memoizedProps = null; this.memoizedState = null; // Hooks 链表 this.updateQueue = null; // 更新队列 // 副作用 this.flags = NoFlags; // 操作标记(Placement/Update/Delete) this.subtreeFlags = NoFlags; this.deletions = null; }
Fiber 使用深度优先遍历(DFS),遵循 child → sibling → return 的顺序:
// 简化的 Fiber 遍历过程 function performUnitOfWork(unitOfWork) { // 1. beginWork:处理当前节点,返回子节点 const next = beginWork(current, unitOfWork); if (next !== null) { // 有子节点,继续向下遍历 workInProgress = next; } else { // 2. completeWork:当前节点完成,开始回溯 completeUnitOfWork(unitOfWork); } } function completeUnitOfWork(unitOfWork) { let completedWork = unitOfWork; do { // 完成当前节点 completeWork(completedWork); // 检查是否有兄弟节点 const siblingFiber = completedWork.sibling; if (siblingFiber !== null) { workInProgress = siblingFiber; // 转向兄弟节点 return; } // 没有兄弟,返回父节点 completedWork = completedWork.return; workInProgress = completedWork; } while (completedWork !== null); }
对于这样的组件树:
App
/ \
Header Content
/ \
Sidebar Main
遍历顺序:App → Header → Content → Sidebar → Main
每个节点会经历 beginWork(向下) 和 completeWork(向上) 两个阶段
React 维护两棵 Fiber 树:
React 的 Diff 算法基于三个假设进行优化:
// 单节点复用判断逻辑 function reconcileSingleElement(returnFiber, currentFirstChild, element) { const key = element.key; let child = currentFirstChild; while (child !== null) { if (child.key === key) { // key 相同 if (child.type === element.type) { // type 也相同 → 复用 const existing = useFiber(child, element.props); existing.return = returnFiber; return existing; } else { // type 不同 → 删除旧节点 deleteChild(returnFiber, child); break; } } else { // key 不同 → 删除 deleteChild(returnFiber, child); } child = child.sibling; } // 新建节点 const created = createFiberFromElement(element); created.return = returnFiber; return created; }
列表 Diff 分三种情况处理:
key 和 type 都相同 → 复用节点,更新 props
新节点在旧节点中不存在 → 创建新 Fiber
旧节点在新节点中不存在 → 标记删除
key 存在但位置变化 → 通过 lastPlacedIndex 判断移动
函数组件的 Hooks 以单向链表形式存储在 Fiber 节点的 memoizedState 上:
// Hook 对象结构 const hook = { memoizedState: null, // 当前状态值(state/effect 等) baseState: null, // 基础状态 baseQueue: null, // 基础更新队列 queue: null, // 更新队列(UpdateQueue) next: null, // 下一个 Hook }; // 更新队列结构(环形链表) const queue = { pending: null, // 待处理的更新(环形链表尾) dispatch: null, // setState 函数 lastRenderedState: null // 上次渲染的状态 };
// useState 本质是 useReducer 的语法糖 function useState(initialState) { const hook = mountWorkInProgressHook(); if (isMount) { // 首次渲染 hook.memoizedState = hook.baseState = typeof initialState === 'function' ? initialState() : initialState; } else { // 更新渲染 hook.memoizedState = hook.baseState; } const queue = (hook.queue = { pending: null, dispatch: null, lastRenderedState: hook.memoizedState, }); const dispatch = (queue.dispatch = dispatchSetState.bind( null, currentlyRenderingFiber, queue )); return [hook.memoizedState, dispatch]; } // dispatch 触发更新 function dispatchSetState(fiber, queue, action) { // 1. 创建 update 对象 const update = { lane: requestUpdateLane(), action, hasEagerState: false, eagerState: null, next: null, }; // 2. 加入更新队列(环形链表) const pending = queue.pending; if (pending === null) { update.next = update; // 指向自己,形成环 } else { update.next = pending.next; pending.next = update; } queue.pending = update; // 3. 调度更新 scheduleUpdateOnFiber(fiber, lane); }
// useEffect 的 memoizedState 存储的是 effect 对象 function mountEffect(create, deps) { return mountEffectImpl( PassiveEffect | HookPassive, HookPassive, create, deps ); } function mountEffectImpl(fiberFlags, hookFlags, create, deps) { const hook = mountWorkInProgressHook(); const nextDeps = deps === undefined ? null : deps; currentlyRenderingFiber.flags |= fiberFlags; hook.memoizedState = pushEffect( HookHasEffect | hookFlags, create, undefined, nextDeps ); } // effect 对象结构 function pushEffect(hookFlags, create, destroy, deps) { const effect = { tag: hookFlags, // 标记是否需要执行 create, // 回调函数 destroy, // 清理函数 deps, // 依赖数组 next: null, // 下一个 effect }; // 添加到 fiber.updateQueue.lastEffect 环形链表 const componentUpdateQueue = currentlyRenderingFiber.updateQueue; if (componentUpdateQueue === null) { componentUpdateQueue = createFunctionComponentUpdateQueue(); currentlyRenderingFiber.updateQueue = componentUpdateQueue; componentUpdateQueue.lastEffect = effect.next = effect; } else { const lastEffect = componentUpdateQueue.lastEffect; if (lastEffect === null) { componentUpdateQueue.lastEffect = effect.next = effect; } else { const firstEffect = lastEffect.next; lastEffect.next = effect; effect.next = firstEffect; componentUpdateQueue.lastEffect = effect; } } return effect; }
Object.is,对于对象和数组每次都是新引用,所以要用 useMemo/useCallback 来稳定引用。
// useRef 是最简单的 Hook,memoizedState 存储 { current: initialValue } function mountRef(initialValue) { const hook = mountWorkInProgressHook(); const ref = { current: initialValue }; hook.memoizedState = ref; return ref; } // useRef 返回的 ref 对象在整个组件生命周期中保持同一个引用 // 所以它常用于: // 1. 访问 DOM 元素 // 2. 保存可变值(不触发重渲染) // 3. 保存定时器 ID 等
React 18 的 Concurrent Mode 通过时间切片实现可中断渲染:
// 简化的 workLoop 实现 function workLoopConcurrent() { // 有工作且未超时,继续执行 while (workInProgress !== null && !shouldYield()) { performUnitOfWork(workInProgress); } } // 每 5ms 检查一次是否需要让出主线程 function shouldYield() { const currentTime = getCurrentTime(); if (currentTime >= deadline) { // 时间片用完,让出主线程 if ((currentTime - lastYieldyStartTime) >= 5ms) { return true; } } return false; }
React 17+ 使用 Lane(车道)模型管理更新优先级:
// Lane 使用 31 位二进制表示优先级 // 数值越小,优先级越高 export const NoLanes: Lanes = /* */ 0b0000000000000000000000000000000; export const NoLane: Lane = /* */ 0b0000000000000000000000000000000; export const SyncLane: Lane = /* */ 0b0000000000000000000000000000001; export const InputContinuousHydrationLane: Lane = /* */ 0b0000000000000000000000000000010; export const InputContinuousLane: Lanes = /* */ 0b0000000000000000000000000000100; export const DefaultHydrationLane: Lane = /* */ 0b0000000000000000000000000001000; export const DefaultLane: Lanes = /* */ 0b0000000000000000000000000010000; export const TransitionHydrationLane: Lane = /* */ 0b0000000000000000000000000100000; export const TransitionLanes: Lanes = /* */ 0b0000000000000000000011111000000; export const RetryLanes: Lanes = /* */ 0b0000000000000000011100000000000; export const SelectiveHydrationLane: Lane = /* */ 0b0000000000000000100000000000000; export const IdleHydrationLane: Lane = /* */ 0b0000000000000001000000000000000; export const IdleLane: Lanes = /* */ 0b0000000000000010000000000000000; export const OffscreenLane: Lane = /* */ 0b0000000000000100000000000000000;
| 优先级 | 场景 | 示例 |
|---|---|---|
| SyncLane | 同步、最高优先级 | legacy 模式 |
| InputContinuousLane | 用户输入 | 滚动、点击 |
| DefaultLane | 默认 | 网络请求回调 |
| TransitionLane | 过渡动画 | startTransition |
| IdleLane | 空闲时 | 离屏内容 |
// startTransition 将回调中的更新标记为 TransitionLane function startTransition(scope) { const prevTransition = ReactCurrentBatchConfig.transition; ReactCurrentBatchConfig.transition = {}; try { scope(); // 执行回调,其中的 setState 会被标记为 TransitionLane } finally { ReactCurrentBatchConfig.transition = prevTransition; } } // 使用示例 function App() { const [input, setInput] = useState(''); const [list, setList] = useState([]); const handleChange = (e) => { setInput(e.target.value); // 高优先级,立即更新 startTransition(() => { setList(generateList(e.target.value)); // 低优先级,可中断 }); }; }
// 使用 useTransition 避免 Suspense 闪烁 function App() { const [tab, setTab] = useState('home'); const [isPending, startTransition] = useTransition(); return ( <> <button onClick={() => setTab('home')}>Home</button> <button onClick={() => { startTransition(() => setTab('posts')); }}>Posts</button> {isPending && <Spinner />} <Suspense fallback={<Loading />}> <TabContent tab={tab} /> </Suspense> </> ); }
function createElement(type, props, ...children) { return { type, props: { ...props, children: children.map(child => typeof child === 'object' ? child : createTextElement(child) ), }, }; } function createTextElement(text) { return { type: "TEXT_ELEMENT", props: { nodeValue: text, children: [], }, }; }
function render(element, container) { const dom = element.type === "TEXT_ELEMENT" ? document.createTextNode("") : document.createElement(element.type); const isProperty = (key) => key !== "children"; Object.keys(element.props) .filter(isProperty) .forEach((name) => { dom[name] = element.props[name]; }); element.props.children.forEach((child) => render(child, dom)); container.appendChild(dom); }
let nextUnitOfWork = null; let currentRoot = null; let wipRoot = null; let deletions = null; function workLoop(deadline) { let shouldYield = false; while (nextUnitOfWork && !shouldYield) { nextUnitOfWork = performUnitOfWork(nextUnitOfWork); shouldYield = deadline.timeRemaining() < 1; } if (!nextUnitOfWork && wipRoot) { commitRoot(); } requestIdleCallback(workLoop); } requestIdleCallback(workLoop); function performUnitOfWork(fiber) { if (typeof fiber.type === 'function') { updateFunctionComponent(fiber); } else { updateHostComponent(fiber); } if (fiber.child) return fiber.child; let nextFiber = fiber; while (nextFiber) { if (nextFiber.sibling) return nextFiber.sibling; nextFiber = nextFiber.return; } }