React 执行阶段与渲染机制详解(基于 React 18+ 官方文档)
🌟 核心概念:React 的执行流程分层
React 的工作流程基于 Fiber 架构 ,将任务拆分为可中断的单元,核心分为 Render Phase(渲染阶段) 和 Commit Phase(提交阶段) ,并辅以 副作用阶段 和 卸载阶段。以下是详细解析:
一、Render Phase(渲染阶段 / 协调阶段)
1. 作用与触发
- 目的:计算新的 Virtual DOM 树,并与旧树进行 Diff(协调算法),决定需要更新的部分。
- 触发条件 :
- 初始挂载(Mount)
- 状态更新(State Update):
useState
/useReducer
- Props 变化(父组件重渲染)
- 强制更新(
forceUpdate
)
2. 特性
- 纯函数性 :组件函数必须是纯函数,禁止修改外部状态或变量 (如
props
、state
、全局变量)。 - 可中断性:React 18 的并发模式下,此阶段可被中断或重启,优先处理高优先级任务。
- Strict Mode 行为 :开发环境下,React 会双调用组件函数,用于检测非纯函数导致的副作用。
3. 代码示例
jsx
function Counter({ time }) {
// 纯函数:相同输入返回相同 JSX
return (
<>
<h1>{time}</h1>
<input /> {/* 用户输入不会因父组件重渲染而丢失 */}
</>
);
}
二、Commit Phase(提交阶段)
1. 作用
将 Render Phase 计算出的变更同步应用到真实 DOM,确保用户界面即时更新。
2. 三个子阶段
- Before Mutation :
- 执行
getSnapshotBeforeUpdate
(类组件) - 执行
useLayoutEffect
的清理函数
- 执行
- Mutation :
- 实际插入、更新、删除 DOM 节点
- Layout :
- 执行
useLayoutEffect
的创建函数 - 类组件的
componentDidMount
/componentDidUpdate
- 执行
3. 关键特性
- 不可中断:此阶段必须一次性完成,确保 DOM 操作的原子性。
- 避免闪烁 :React 仅更新差异节点(Diff 结果),保证
input
值等用户交互状态不丢失。
三、Passive Effect Phase(被动副作用阶段)
1. 作用
在浏览器完成布局和绘制后,异步执行副作用(如网络请求、订阅)。
2. 执行内容
useEffect
的创建函数和清理函数- 类组件的
componentDidMount
/componentDidUpdate
/componentWillUnmount
中的异步操作
3. 与 useLayoutEffect
的区别
Hook | 执行时机 | 是否阻塞渲染 | 适用场景 |
---|---|---|---|
useEffect |
Passive Phase | ❌ 否 | 数据获取、日志、非关键副作用 |
useLayoutEffect |
Commit Phase(Layout 阶段) | ✅ 是 | 同步 DOM 操作(如测量尺寸) |
四、Unmount Phase(卸载阶段)
1. 触发条件
组件从 DOM 中移除(如条件渲染切换、列表项删除)。
2. 执行内容
- 清理
useEffect
/useLayoutEffect
的副作用 - 类组件的
componentWillUnmount
五、状态保留与重置机制
1. 状态隔离规则
- 相同位置保留状态 :组件在树中的位置不变时,React 保留其
state
。 - 位置变化重置状态:组件位置变化时,React 会销毁旧组件并初始化新组件(即使类型相同)。
2. 控制状态保留的技巧
- Key 属性 :通过唯一
key
标识列表项,避免位置变化导致的状态重置。 - 条件渲染:动态切换组件类型时,状态会被重置(即使 props 相同)。
jsx
function App() {
const [isFancy, setIsFancy] = useState(false);
return (
<div>
{isFancy ? <Counter isFancy={true} /> : <Counter isFancy={false} />}
</div>
);
}
上述示例中,
<Counter />
的位置不变,因此score
状态会保留。
六、错误处理机制(Error Boundaries)
1. 执行阶段
getDerivedStateFromError
:同步执行,用于渲染降级 UI。componentDidCatch
:提交阶段执行,用于记录错误日志。
2. 限制
- 无法捕获的错误 :事件处理器、异步代码(如
setTimeout
)、服务端渲染(SSR)中的错误。
七、性能优化策略
1. 避免过早优化
- 默认行为:React 的 Diff 算法已足够高效,优先保证代码可读性。
- 优化时机 :当性能瓶颈明显时,使用
React.memo
、useMemo
、useCallback
等工具。
2. 优化技巧
- 减少不必要的 re-renders :使用
React.memo
包裹子组件。 - 避免在 Render Phase 创建对象/函数 :使用
useMemo
/useCallback
缓存。 - 并发模式下的优先级调度:React 自动优化任务优先级,无需手动干预。
八、官方文档与术语参考
- Render Phase :React 官方文档
- Commit Phase :React 官方文档
- useEffect vs useLayoutEffect :React 官方文档
- 状态保留与 Key :React 官方文档
九、总结:React 执行流程全貌
csharp
用户交互 / 状态更新
↓
[Render Phase](协调)
↓
[Commit Phase]
├── Before Mutation → useInsertionEffect / getSnapshotBeforeUpdate
├── Mutation → DOM 更新
└── Layout → useLayoutEffect / componentDidMount/Update
↓
[Passive Effect Phase] → useEffect
↓
[Unmount Phase] → 清理副作用
掌握这一流程,能帮助你编写更符合 React 设计哲学的代码,避免常见陷阱(如非纯函数、滥用 useLayoutEffect
),并充分利用 React 的并发能力提升应用性能。