面试题
- react有哪几种任务优先级?
- react hook的原理以及为什么只能在写在组件里,不能写在if else里面?
一、整体架构概览
React 的核心可以比喻成一条流水线工厂:
arduino
用户操作/状态更新
↓
┌─────────────────┐
│ Scheduler │ ← 调度器:决定什么时候工作(优先级管理)
│ (时间切片) │
└────────┬────────┘
↓md
┌─────────────────┐
│ Reconciler │ ← 协调器:决定做什么工作(Diff 比较)
│ (Fiber 架构) │
└────────┬────────┘
↓
┌─────────────────┐
│ Renderer │ ← 渲染器:实际执行工作(DOM 操作)
│ (平台相关) │
└────────┬────────┘
↓
更新界面
三大核心模块:
- Scheduler - 调度器:管理任务的优先级和执行时机
- Reconciler - 协调器:计算新老虚拟 DOM 的差异(Diff)
- Renderer - 渲染器:将计算结果应用到真实 DOM
二、Scheduler(调度器)- 聪明的时间管理者
2.1 为什么需要 Scheduler?
想象一下,如果 React 一次性处理大量更新,浏览器就会卡住(掉帧)。Scheduler 就像一个智能管家,把大任务切成小块,让浏览器有时间绘制页面。
2.2 核心概念
javascript
// 任务优先级(从高到低)
ImmediatePriority // 立即执行,如用户输入
UserBlockingPriority // 用户阻塞级,如点击、滚动
NormalPriority // 正常优先级,如网络请求回调
LowPriority // 低优先级,如分析统计
IdlePriority // 空闲时才执行
2.3 核心函数流程
scss
scheduleCallback(priority, callback)
↓
┌─────────────────┐
│ 创建任务对象 │
│ { callback, │
│ priority, │
│ expirationTime│
│ } │
└────────┬────────┘
↓
┌─────────────────┐
│ 放入任务队列 │
│ (最小堆结构) │
└────────┬────────┘
↓
┌─────────────────┐
│ requestIdleCallback│
│ 或 MessageChannel │ ← 浏览器空闲时执行
└────────┬────────┘
↓
flushWork()
↓
workLoop(hasTimeRemaining)
2.4 时间切片(Time Slicing)原理
javascript
// 简化的工作循环
function workLoop(hasTimeRemaining) {
while (currentTask !== null && hasTimeRemaining()) {
// 执行当前任务
currentTask.callback();
// 检查时间是否够用(默认 5ms)
if (!hasTimeRemaining()) {
// 时间不够了,让出主线程
break;
}
}
// 还有任务没完成,继续调度
if (currentTask !== null) {
scheduleCallback(currentTask.priority, workLoop);
}
}
通俗解释:
- 就像吃面条,如果一口气吃完会噎着
- Scheduler 把面条分成小段,吃一口歇一口
- 浏览器每 16.6ms 渲染一帧,React 利用空闲时间工作
三、Reconciler(协调器)- 精明的差异计算师
3.1 什么是 Reconciler?
Reconciler 是 React 的大脑,负责比较新老虚拟 DOM,找出最小化的变更操作。
3.2 Fiber 架构
React 16 之前使用递归遍历,可能会卡死主线程。Fiber 把递归改成链表遍历,可以随时中断和恢复。
javascript
// Fiber 节点结构
interface Fiber {
// 类型信息
tag: WorkTag; // 组件类型(函数组件、类组件、HostDOM 等)
type: any; // 具体的类型(如 'div' 或函数)
key: null | string; // diff 的 key
// 树结构(链表)
return: Fiber | null; // 父节点
child: Fiber | null; // 第一个子节点
sibling: Fiber | null; // 下一个兄弟节点
// 状态
pendingProps: any; // 新 props
memoizedProps: any; // 当前 props
memoizedState: any; // 当前 state / hooks 链表头
// 副作用
flags: Flags; // 标记需要执行的操作(插入、更新、删除)
updateQueue: any; // 更新队列
// 双缓冲
alternate: Fiber | null; // 指向 current tree 的对应节点
}
3.3 双缓冲机制(Double Buffering)
css
Current Tree (当前显示) WorkInProgress Tree (正在构建)
↓ ↓
┌──────┐ ┌──────┐
│ Div │ ← alternate → │ Div │
└──┬───┘ └──┬───┘
│ │
┌──┴──┐ ┌──┴──┐
│ Span│ ← alternate → │ Span│
└─────┘ └─────┘
更新完成后:
WorkInProgress Tree 变成 Current Tree
(只需切换 root 指针,原子操作)
3.4 Reconciler 核心流程
scss
开始更新
↓
┌─────────────────┐
│ createUpdate() │ ← 创建更新对象
└────────┬────────┘
↓
┌─────────────────┐
│ enqueueUpdate() │ ← 加入组件的更新队列
└────────┬────────┘
↓
┌─────────────────┐
│ scheduleUpdateOnFiber() │ ← 调度更新
└────────┬────────┘
↓
┌─────────────────┐
│ performSyncWorkOnRoot │ ← 同步执行(高优先级)
│ 或 │
│ performConcurrentWorkOnRoot │ ← 并发执行(可中断)
└────────┬────────┘
↓
┌─────────────────┐
│ renderRootSync / │ ← 进入 render 阶段
│ renderRootConcurrent
└────────┬────────┘
↓
┌─────────────────┐
│ workLoopSync / │ ← 工作循环
│ workLoopConcurrent
└────────┬────────┘
↓
performUnitOfWork() ← 处理每个 Fiber 节点(循环)
↓
beginWork() ← 向下遍历,创建/复用 Fiber
↓
completeWork() ← 向上回溯,收集副作用
↓
commitRoot() ← 进入 commit 阶段
3.5 详细函数说明
performUnitOfWork - 工作单元执行
javascript
function performUnitOfWork(unitOfWork: Fiber): Fiber | null {
// 1. 处理当前节点(创建/复用子 Fiber)
const next = beginWork(unitOfWork);
// 2. 如果有子节点,继续向下
if (next !== null) {
return next;
}
// 3. 没有子节点,开始向上回溯
let completedWork = unitOfWork;
do {
// 完成当前节点的处理
completeWork(completedWork);
// 4. 如果有兄弟节点,处理兄弟
const siblingFiber = completedWork.sibling;
if (siblingFiber !== null) {
return siblingFiber;
}
// 5. 否则继续向上
completedWork = completedWork.return;
} while (completedWork !== null);
return null;
}
beginWork - 向下遍历阶段
javascript
function beginWork(current, workInProgress, renderLanes) {
const tag = workInProgress.tag;
switch (tag) {
case FunctionComponent:
// 函数组件:执行函数,获取返回值
return updateFunctionComponent(current, workInProgress);
case ClassComponent:
// 类组件:执行 render 方法
return updateClassComponent(current, workInProgress);
case HostRoot:
// 根节点
return updateHostRoot(current, workInProgress);
case HostComponent:
// 原生 DOM(div, span 等)
return updateHostComponent(current, workInProgress);
case HostText:
// 文本节点
return updateHostText(current, workInProgress);
// ... 其他类型
}
}
completeWork - 向上回溯阶段
javascript
function completeWork(current, workInProgress) {
const tag = workInProgress.tag;
switch (tag) {
case HostComponent:
// 创建/更新 DOM 节点(但不挂载)
const instance = createInstance(type, props);
workInProgress.stateNode = instance;
break;
case HostText:
// 创建文本节点
const textInstance = createTextInstance(newText);
workInProgress.stateNode = textInstance;
break;
}
// 收集副作用,构建 effect 链表
bubbleProperties(workInProgress);
}
四、Renderer(渲染器)- 勤劳的 DOM 操作员
4.1 Renderer 的职责
Renderer 是实际干活的模块,负责把 Reconciler 计算出的变更应用到真实 DOM。
4.2 Commit 阶段(同步不可中断)
markdown
┌─────────────────────────────────────────────────────┐
│ commitRoot │
│ (同步执行) │
└─────────────────────────────────────────────────────┘
↓
┌────────────────────┼────────────────────┐
↓ ↓ ↓
┌─────────┐ ┌─────────────┐ ┌─────────────┐
│ Before │ │ Mutation │ │ Layout │
│ Mutation│ → │ (变更) │ → │ (布局) │
│ │ │ │ │ │
│ 准备阶段 │ │ 操作真实 DOM │ │ 读取布局信息 │
│ 触发快照 │ │ │ │ 执行副作用 │
│ 触发 will│ │ │ │ 调用 did │
│ 生命周期 │ │ │ │ 生命周期 │
└─────────┘ └─────────────┘ └─────────────┘
4.3 Commit 阶段详细流程
阶段一:Before Mutation
javascript
// 执行 getSnapshotBeforeUpdate
// 可以在 DOM 变更前获取一些信息(如滚动位置)
阶段二:Mutation(核心变更阶段)
javascript
function commitMutationEffects(root, renderPriorityLevel) {
while (nextEffect !== null) {
const flags = nextEffect.flags;
// 1. 插入新节点
if (flags & Placement) {
commitPlacement(nextEffect);
}
// 2. 更新属性
if (flags & Update) {
commitWork(nextEffect);
}
// 3. 删除节点
if (flags & Deletion) {
commitDeletion(nextEffect);
}
nextEffect = nextEffect.nextEffect;
}
}
// 插入节点的实际操作
function commitPlacement(finishedWork) {
const parentFiber = getHostParentFiber(finishedWork);
const parent = parentFiber.stateNode;
const node = finishedWork.stateNode;
// 真正的 DOM 操作!
parent.appendChild(node);
}
阶段三:Layout
javascript
function commitLayoutEffects(root, finishedWork) {
while (nextEffect !== null) {
const flags = nextEffect.flags;
// 1. 执行 useLayoutEffect
if (flags & Update) {
commitHookEffectListMount(Layout, nextEffect);
}
// 2. 调用 componentDidMount / componentDidUpdate
if (flags & Callback) {
safelyCallComponentDidUpdate(nextEffect);
}
// 3. 执行 setState 的回调
if (flags & Ref) {
commitAttachRef(nextEffect);
}
nextEffect = nextEffect.nextEffect;
}
}
// 最后执行 useEffect(异步)
scheduleCallback(NormalPriority, () => {
flushPassiveEffects();
});
五、Hooks 原理 - 魔法背后的秘密
5.1 Hooks 的本质
Hooks 本质上就是函数,它们利用闭包和链表来存储状态。
5.2 Fiber 上的 Hooks 存储
javascript
// Fiber 节点中存储 Hooks 的地方
fiber.memoizedState = {
// 指向第一个 Hook
memoizedState: value, // Hook 当前值
queue: updateQueue, // 更新队列
next: nextHook // 指向下一个 Hook(形成链表)
}
5.3 Hook 的数据结构
javascript
interface Hook {
memoizedState: any; // 状态值(useState)或 effect 对象(useEffect)
baseState: any; // 基础状态(用于处理跳过的更新)
baseQueue: Update | null; // 基础更新队列
queue: UpdateQueue | null; // 当前更新队列
next: Hook | null; // 下一个 Hook
}
5.4 Hooks 执行流程
scss
函数组件执行
↓
┌─────────────────┐
│ renderWithHooks │ ← 初始化 Hooks 上下文
└────────┬────────┘
↓
执行 FunctionComponent()
↓
┌─────────────────┐
│ useState() │ ← 或 useEffect/useMemo 等
└────────┬────────┘
↓
┌─────────────────┐
│ mountState() │ ← 首次渲染
│ 或 updateState()│ ← 更新渲染
└────────┬────────┘
↓
返回 [state, dispatch]
↓
继续执行组件函数
↓
finishRender()
↓
调度 Effects(useEffect)
5.5 useState 详细实现
javascript
// ========== 首次渲染 ==========
function mountState(initialState) {
// 1. 创建 Hook 对象
const hook = mountWorkInProgressHook();
// 2. 初始化状态
hook.memoizedState = hook.baseState = initialState;
// 3. 创建更新队列
const queue = {
pending: null, // 待处理的更新
dispatch: null, // dispatch 函数
lastRenderedReducer: basicStateReducer,
lastRenderedState: initialState,
};
hook.queue = queue;
// 4. 创建 dispatch 函数(绑定当前 fiber 和 queue)
const dispatch = dispatchSetState.bind(
null,
currentlyRenderingFiber,
queue
);
queue.dispatch = dispatch;
return [hook.memoizedState, dispatch];
}
// ========== 更新渲染 ==========
function updateState(initialState) {
// 1. 获取或创建 Hook
const hook = updateWorkInProgressHook();
// 2. 处理更新队列
const queue = hook.queue;
if (queue.pending !== null) {
// 有需要处理的更新
const newState = processUpdateQueue(hook, queue);
hook.memoizedState = newState;
}
return [hook.memoizedState, queue.dispatch];
}
// ========== 触发更新 ==========
function dispatchSetState(fiber, queue, action) {
// 1. 创建更新对象
const update = {
lane: requestUpdateLane(fiber), // 优先级车道
action: action, // 新值或更新函数
eagerReducer: null,
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. 发起调度
const root = enqueueConcurrentRenderForLane(fiber, lane);
scheduleUpdateOnFiber(root, fiber, lane);
}
5.6 useEffect 详细实现
javascript
// Effect 对象结构
interface Effect {
tag: HookFlags; // 标记(Passive, HasEffect 等)
create: () => (() => void) | void; // 副作用函数
destroy: (() => void) | void; // 清理函数
deps: Array<any> | null; // 依赖数组
next: Effect; // 下一个 Effect(循环链表)
}
// ========== 首次渲染 ==========
function mountEffect(create, deps) {
return mountEffectImpl(
PassiveEffect | HookPassive, // 标记为 Passive(异步执行)
HookPassive,
create,
deps
);
}
function mountEffectImpl(fiberFlags, hookFlags, create, deps) {
const hook = mountWorkInProgressHook();
// 创建 effect 对象
const effect: Effect = {
tag: hookFlags,
create,
destroy: undefined,
deps,
next: null,
};
// 挂载到 Fiber 的 updateQueue
hook.memoizedState = pushEffect(hookFlags, create, undefined, deps);
}
// ========== 更新渲染 ==========
function updateEffect(create, deps) {
return updateEffectImpl(
PassiveEffect | HookPassive,
HookPassive,
create,
deps
);
}
function updateEffectImpl(fiberFlags, hookFlags, create, deps) {
const hook = updateWorkInProgressHook();
const prevEffect = hook.memoizedState;
// 检查依赖是否变化
if (areHookInputsEqual(deps, prevEffect.deps)) {
// 依赖没变,跳过执行
hook.memoizedState = pushEffect(hookFlags, create, prevEffect.destroy, deps);
return;
}
// 依赖变了,标记需要执行
hook.memoizedState = pushEffect(
HookHasEffect | hookFlags,
create,
prevEffect.destroy,
deps
);
}
// ========== 执行 Effects ==========
function flushPassiveEffects() {
// 1. 先执行上一次的清理函数(destroy)
commitHookEffectListUnmount(HookPassive | HookHasEffect);
// 2. 再执行这一次的副作用函数(create)
commitHookEffectListMount(HookPassive | HookHasEffect);
}
5.7 Hook 链表的形成
javascript
// 每次调用 Hook 都会创建一个新的 Hook 节点
function mountWorkInProgressHook() {
const hook = {
memoizedState: null,
baseState: null,
baseQueue: null,
queue: null,
next: null,
};
if (workInProgressHook === null) {
// 第一个 Hook
currentlyRenderingFiber.memoizedState = workInProgressHook = hook;
} else {
// 链接到链表末尾
workInProgressHook = workInProgressHook.next = hook;
}
return workInProgressHook;
}
// Hook 调用顺序形成链表
function MyComponent() {
const [state1] = useState(0); // Hook 1 ← memoizedState
const [state2] = useState(1); // Hook 2 ← next
const [state3] = useState(2); // Hook 3 ← next
useEffect(() => {}, []); // Hook 4 ← next
// ...
}
为什么 Hooks 不能放在 if 语句里? 因为 React 靠调用顺序来匹配 Hook,如果用 if,会导致顺序错乱,Hook 对应关系就乱了!
六、完整流程串联
6.1 首次渲染流程
scss
ReactDOM.createRoot(container).render(<App />)
↓
创建 FiberRoot 和 RootFiber
↓
updateContainer(element)
↓
scheduleUpdateOnFiber(fiber)
↓
┌─────────────────────────────────────────────────────┐
│ Scheduler │
│ requestIdleCallback / MessageChannel │
│ ↓ │
│ performConcurrentWorkOnRoot │
└─────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────┐
│ Reconciler │
│ renderRootConcurrent / renderRootSync │
│ ↓ │
│ workLoopConcurrent / workLoopSync │
│ ↓ │
│ performUnitOfWork() 循环 │
│ ↓ │
│ beginWork(): 创建 Fiber 树 │
│ ↓ │
│ completeWork(): 创建 DOM 节点(不插入) │
│ ↓ │
│ 构建 effect 链表 │
└─────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────┐
│ Renderer │
│ commitRoot(root) │
│ ↓ │
│ ┌──────────────────────────────────────────────┐ │
│ │ commitBeforeMutationEffects │ │
│ │ - 执行 getSnapshotBeforeUpdate │ │
│ └──────────────────────────────────────────────┘ │
│ ↓ │
│ ┌──────────────────────────────────────────────┐ │
│ │ commitMutationEffects │ │
│ │ - 插入 DOM 节点 │ │
│ │ - 更新 DOM 属性 │ │
│ └──────────────────────────────────────────────┘ │
│ ↓ │
│ ┌──────────────────────────────────────────────┐ │
│ │ commitLayoutEffects │ │
│ │ - 执行 useLayoutEffect │ │
│ │ - 调用 componentDidMount │ │
│ │ - 绑定 ref │ │
│ └──────────────────────────────────────────────┘ │
│ ↓ │
│ 调度 useEffect(异步执行) │
└─────────────────────────────────────────────────────┘
↓
页面显示完成!
6.2 状态更新流程
scss
用户点击按钮 → setState(newValue)
↓
dispatchSetState(fiber, queue, action)
↓
创建 Update 对象,加入队列
↓
scheduleUpdateOnFiber(fiber)
↓
┌─────────────────────────────────────────────────────┐
│ Scheduler │
│ 根据优先级决定是否立即执行或延迟 │
│ 高优先级:立即执行(输入、点击) │
│ 低优先级:进入时间切片调度 │
└─────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────┐
│ Reconciler │
│ 创建 workInProgress 树(alternate) │
│ ↓ │
│ 复用 current 树的可复用节点 │
│ ↓ │
│ beginWork: 对比 props,标记 flags │
│ ↓ │
│ 如果有子组件:递归处理 │
│ ↓ │
│ completeWork: 收集副作用 │
└─────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────┐
│ Renderer │
│ commitRoot(同步执行,不可中断) │
│ ↓ │
│ 执行 DOM 变更 │
│ ↓ │
│ 执行副作用(useLayoutEffect) │
│ ↓ │
│ 调度 useEffect(异步) │
└─────────────────────────────────────────────────────┘
↓
更新完成!
七、图解核心概念
7.1 时间切片
arduino
时间 →
├──────┼──────┼──────┼──────┼──────┼──────┼──────┤
│ 帧1 │ 帧2 │ 帧3 │ 帧4 │ 帧5 │ 帧6 │ 帧7 │
│ │ │ │ │ │ │ │
│工作1 │工作2 │工作3 │工作4 │工作5 │工作6 │工作7 │
└──────┴──────┴──────┴──────┴──────┴──────┴──────┘
没有 Scheduler(卡顿):
├─────────────────────────────────────────────────┤
│ 一次性完成所有工作 │
│ (浏览器无法渲染,界面卡住) │
└─────────────────────────────────────────────────┘
有了 Scheduler(流畅):
├──────┼──────┼──────┼──────┼──────┼──────┼──────┤
│工作1 │ 渲染 │工作2 │ 渲染 │工作3 │ 渲染 │工作4 │
└──────┴──────┴──────┴──────┴──────┴──────┴──────┘
7.2 Fiber 链表遍历
css
树结构 遍历顺序
A A
/ \ ↓
B C ====> B
/ \ \ ↓
D E F D
↑ ↓
完成 D 后找兄弟 E
↓
完成 E 后找兄弟 B(回溯)
↓
完成 B 后找兄弟 C
↓
完成 C 后找兄弟 F
7.3 Hooks 链表
scss
function Component() {
const [a] = useState(1); // Hook 0
const [b] = useState(2); // Hook 1
const ref = useRef(3); // Hook 2
useEffect(() => {}, []); // Hook 3
}
Fiber.memoizedState
↓
┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐
│ useState│ → │ useState│ → │ useRef │ → │useEffect│
│ hook0 │ │ hook1 │ │ hook2 │ │ hook3 │
└────────┘ └────────┘ └────────┘ └────────┘
memoizedState:1 memoizedState:2 memoizedState: memoizedState:
{current:3} effect对象
八、总结
| 模块 | 职责 | 核心特点 |
|---|---|---|
| Scheduler | 调度任务执行时机 | 时间切片、优先级管理、可中断 |
| Reconciler | 计算虚拟 DOM 差异 | Fiber 架构、双缓冲、可中断恢复 |
| Renderer | 操作真实 DOM | 同步执行、分阶段处理 |
| Hooks | 函数组件状态管理 | 链表存储、闭包捕获、按顺序执行 |
核心设计思想
- 可中断的渲染:把大任务拆小,避免阻塞主线程
- 双缓冲技术:两棵树交替工作,保证一致性
- 优先级调度:重要更新优先执行
- 副作用分离:DOM 变更和副作用执行分开处理
这就是 React 能处理复杂界面并保持流畅的秘密!🎉