#金石焕新程
🚀《React 更新界面全流程:从 setState 到 像素上屏》
面试常被追问:
"React 更新界面到底经历了哪些阶段?"
本文用一张图 + 一段可运行代码,带你走通 React 18(含并发特性) 的完整刷新链路。
📊 总览:一次更新 6 步曲
阶段 | 发生位置 | 关键 API |
---|---|---|
① 触发更新 | 开发者代码 | setState / dispatch |
② 构造更新对象 | React 内部 | Update |
③ 调度 & 优先级 | Fiber Scheduler | scheduleUpdateOnFiber |
④ Render 阶段(可中断) | Fiber | beginWork → completeWork |
⑤ Commit 阶段(同步) | DOM | commitWork → commitMutationEffects |
⑥ 浏览器绘制 | 浏览器 | requestAnimationFrame → 像素上屏 |
① 触发更新:setState 不是立刻刷 DOM!
jsx
function Counter() {
const [count, setCount] = useState(0);
// 触发更新
const handleClick = () => setCount(c => c + 1);
return <button onClick={handleClick}>{count}</button>;
}
setCount
只是把 更新任务 放进 Fiber 队列,不会立即渲染。
② 构造更新对象(Fiber 视角)
js
// React 内部伪代码
const update = {
lane: SyncLane, // 优先级
action: c => c + 1, // 更新函数
next: null // 链表结构
};
fiber.updateQueue = appendUpdate(fiber.updateQueue, update);
③ 调度 & 优先级(时间切片)
js
// React 18 并发调度
scheduleUpdateOnFiber(rootFiber, lane);
- 高优先级任务 (用户输入)插队 ,低优先级任务让步。
- 浏览器空闲时继续可中断渲染。
④ Render 阶段:Fiber 可中断遍历
plaintext
beginWork → completeWork
- 可中断:长列表渲染可分段进行(避免白屏)。
- 双缓冲:一边渲染 Fiber 树,一边复用旧树。
⑤ Commit 阶段:同步提交到 DOM
plaintext
commitWork
├─ commitMutationEffects // 插入 / 更新 / 删除 DOM
├─ commitLayoutEffects // 调用 useLayoutEffect
└─ commitPassiveEffects // 调用 useEffect
- 同步不可中断,确保 DOM 与 React 树一致。
⑥ 浏览器绘制:像素上屏
- React 把
style
或transform
交给浏览器。 - 浏览器在下一帧 VSync 信号后完成绘制。
🧪 实战:打断渲染(Concurrent Feature)
jsx
import { startTransition } from 'react';
function Search() {
const [query, setQuery] = useState('');
const [list, setList] = useState([]);
const handleChange = (e) => {
const q = e.target.value;
setQuery(q);
startTransition(() => {
// 低优先级更新:长列表过滤
setList(filterList(q));
});
};
return (
<>
<input value={query} onChange={handleChange} />
{list.map(item => <li key={item.id}>{item.name}</li>)}
</>
);
}
startTransition
把长列表过滤标记为低优先级 ,高优先级输入不卡顿。
🔍 调试技巧:浏览器 Performance 面板
- 火焰图:查看 Render、Commit 耗时。
- React DevTools Profiler:识别重渲染组件。
- Paint Flashing:高亮重绘区域。
🏁 一句话总结
React 更新 = 调度优先级 + 可中断渲染 + 同步提交 DOM + 浏览器绘制 ,
记住 6 步曲,面试问「React 怎么更新界面」直接答:
"从 setState 触发 → Fiber 调度 → Commit → 像素上屏!"