跳到主要内容

React 源码解析(Fiber & Reconciler)

源码核心模块

1. Fiber 数据结构

type Fiber = {
tag: WorkTag // 节点类型(FunctionComponent/ClassComponent/HostComponent...)
key: null | string
type: any // 对应 createElement 的第一个参数
stateNode: any // DOM 节点 或 class 实例

return: Fiber | null // 父节点
child: Fiber | null // 第一个子节点
sibling: Fiber | null // 下一个兄弟节点

pendingProps: any // 本次渲染的 props
memoizedProps: any // 上次渲染的 props
memoizedState: any // hooks 链表的头节点

flags: Flags // 副作用标记(Placement/Update/Deletion)
subtreeFlags: Flags
updateQueue: any // 状态更新队列
}

2. 双缓冲机制

  • React 同时维护两棵 Fiber 树:current tree(当前屏幕)和 workInProgress tree(构建中)
  • 渲染完成后 root.current = workInProgress,两棵树交替复用
  • 好处:构建过程可中断,不影响当前显示内容

3. Reconcile 阶段(beginWork & completeWork)

深度优先遍历 Fiber

beginWork:根据 type 处理节点(diff children,打 flags)

若有子节点 → 递归处理子节点
若无子节点 → completeWork:创建/更新 DOM,收集 subtreeFlags

返回父节点继续 completeWork

Diff 策略

  1. 单节点 Diff:key 和 type 都相同 → 复用;否则销毁重建
  2. 多节点 Diff:两轮遍历
    • 第一轮:按顺序对比,遇到 key 不同则停止
    • 第二轮:将剩余旧节点放入 Map,用 key 查找可复用节点
    • 最长递增子序列优化移动操作(React 18)

4. Commit 阶段(三个子阶段)

子阶段工作内容
BeforeMutation读取 DOM 快照,调用 getSnapshotBeforeUpdate
Mutation操作真实 DOM(增/改/删),调用 useLayoutEffect 清理
Layout调用 useLayoutEffect 回调,更新 ref
  • useEffect 在 commit 后异步调度,不阻塞浏览器绘制
  • useLayoutEffect 在 Layout 阶段同步执行,会阻塞绘制

5. Hooks 实现原理

Fiber.memoizedStateHook₁Hook₂Hook₃ (链表)

{ memoizedState, queue, next }
  • mount 阶段:每个 useState 在链表末尾插入新 Hook 节点
  • update 阶段:按顺序取链表节点,计算新状态
  • 为什么不能条件渲染 Hooks:顺序错位会取到错误的 Hook 节点

6. 调度器(Scheduler)

  • 基于 MessageChannel 实现任务切片(非 requestAnimationFrame)
  • 每帧预算 5ms,超出则中断,让出主线程
  • 优先级队列:ImmediatePriority > UserBlockingPriority > NormalPriority > IdlePriority
  • lane 模型(React 18):用位运算表示优先级,支持批量处理多个优先级

7. 高频面试题

  • 为什么要用 MessageChannel 而非 setTimeout? setTimeout 最小延迟 ~4ms,MessageChannel 延迟更低
  • useEffect 清理函数什么时候执行? 下次 effect 执行前 或 组件 unmount 时
  • Concurrent Mode 如何保证状态一致性? 同一优先级的更新批量处理,低优先级更新被高优先级打断后重新计算