React 渲染流程
从 JSX 写到屏幕上显示,React 经过编译 → 协调 → 提交三个大阶段。理解这条链路是面试高频考点,也能帮你定位性能问题。
JSX
↓ Babel 编译
React.createElement()
↓ 返回 JS 对象
Virtual DOM(React Element)
↓ Fiber 架构
Fiber Tree
↓ Render 阶段(Diff)
Effect List
↓ Commit 阶段(不可中断)
真实 DOM 更新
↓
浏览器渲染页面一、JSX → Virtual DOM
JSX 不是合法的 JS,浏览器不认识它。Babel 先把 JSX 解析成 AST(抽象语法树),再编译为函数调用:
// 你写的 JSX
const el = <div className="box">hello</div>
// Babel 编译后(React 17 前)
const el = React.createElement('div', { className: 'box' }, 'hello')
// React 17+ 自动导入
const el = jsx('div', { className: 'box', children: 'hello' })createElement 返回的不是 DOM,而是一个普通的 JS 对象——这就是 Virtual DOM(也叫 React Element):
export default {
type: 'div',
props: { className: 'box', children: 'hello' },
}React 用这个 JS 对象描述"页面应该长什么样",而不是直接操作昂贵的真实 DOM。
二、Virtual DOM → Fiber Tree
React 不会直接 diff Virtual DOM,而是先把它转换成 Fiber Tree——每个组件/元素对应一个 Fiber 节点:
// Fiber 节点核心字段
{
type, // 组件类型
stateNode, // 对应的真实 DOM
child, // 第一个子节点
sibling, // 下一个兄弟节点
return, // 父节点
flags, // 标记(Placement / Update / Deletion)
}Fiber 解决的核心问题:React 16 之前使用递归 diff,无法中断,大页面会卡顿。Fiber 把渲染任务拆成小单元,变成可中断的链表遍历,支持暂停、恢复和优先级调度。
三、Render 阶段(Diff)
Render 阶段的核心任务:对比新旧 Fiber Tree,找出变化。这个阶段可以中断,React 会按优先级分片执行。
Diff 三条规则
React 的 Diff 算法遵循以下三条规则:
| 规则 | 判断条件 | React 行为 |
|---|---|---|
| 节点类型不同 | div → p | 销毁旧节点,创建新节点 |
| 节点类型相同 | div.a → div.b | 复用 DOM,只更新属性 |
| 列表靠 key 识别 | key 相同 | 复用节点;无 key 则暴力按序 diff |
React Diff 是同层比较,不会跨层级。复杂度从 O(n³) 降到 O(n)。
Diff 完成后,React 把需要变更的节点记录为 Effect List(Placement / Update / Deletion),交给下一阶段。
四、Commit 阶段
Commit 阶段不可中断,React 在此阶段真正操作真实 DOM:
- 创建 DOM —
document.createElement() - 更新属性 —
dom.className = 'box' - 插入节点 —
parent.appendChild() - 删除节点 —
removeChild() - 执行副作用 —
useEffect、componentDidMount、componentDidUpdate等生命周期
DOM 更新完成后,浏览器执行 style → layout → paint → composite 的渲染管线,最终显示页面。
五、一次 setState 发生了什么
这是面试高频题。调用 setCount(count + 1) 后:
1. 创建更新对象 { action: count + 1 }
2. 放入更新队列(UpdateQueue)
3. Scheduler 调度(决定同步/过渡/低优先级)
4. 函数组件重新执行 → 生成新 Virtual DOM
5. Diff 新旧 Fiber Tree → 生成 Effect List
6. Commit → 更新真实 DOM多个 setState 会被 React 合并在同一批次处理,减少重复渲染。
六、React 为什么快
React 的快不是因为 Virtual DOM 本身,而是这几个机制叠加的结果:
- 批量更新 — 合并多个 setState,减少 DOM 操作次数。
- Diff 优化 — 同层比较 + key,将复杂度降到 O(n)。
- Fiber 可中断 — 避免长时间阻塞主线程,保持页面响应。
- 增量更新 — 只更新变化的部分,不全量重绘。
七、 一句话总结
JSX 经 Babel 编译为
React.createElement调用,生成 Virtual DOM(JS 对象)。React 把它转成 Fiber Tree,在 Render 阶段通过同层 Diff 找出变化,记录为 Effect List;在 Commit 阶段一次性操作真实 DOM,由浏览器完成渲染。Fiber 架构让这个过程可中断、可调度。