React:Render vs LayoutEffect vs Effect
React Hooks Execution Timing: Render vs LayoutEffect vs Effect
在 React 函数组件中,逻辑代码的执行时机主要分为三个阶段。理解它们的区别对于优化性能和避免 Bug 至关重要。
1. 核心对比表格 (Comparison Table)
| 方式 | 执行时机 | 所处阶段 | 阻塞 UI 绘制? | 核心应用场景 |
|---|---|---|---|---|
| 函数体直接执行(Direct Execution) | 组件函数被调用时,Virtual DOM 构建期间 | Render Phase | 是 (计算耗时会导致卡顿) | 派生状态计算 (Derived State)、简单过滤、常量定义 |
| useLayoutEffect | DOM 更新完成,但浏览器把内容画到屏幕之前 | Commit Phase (Pre-paint) | 是 (同步执行) | DOM 测量、读取布局、为了防止画面闪烁的样式修正 |
| useEffect | DOM 更新完成,且浏览器已经把内容画到屏幕之后 | Commit Phase (Post-paint) | 否 (异步执行) | 副作用处理 (API 请求、埋点、订阅、定时器) |
2. 执行流程图 (Execution Flow)
graph TD
A[组件 State/Props 改变] --> B(React 调用组件函数)
B -- 1. 函数体直接执行 --> C[构建 Virtual DOM]
C --> D[React 更新真实 DOM]
D -- 2. useLayoutEffect 执行 --> E{浏览器绘制 Paint}
E --> F[用户看到新界面]
F -- 3. useEffect 执行 --> G[后续逻辑]
style B fill:#f9f,stroke:#333
style D fill:#bbf,stroke:#333
style E fill:#ff9,stroke:#333
3. 详细场景说明 (Detailed Scenarios)
3.1 直接在函数体中执行 (Render Phase)
这是 React 构建 Virtual DOM 的过程。每次组件更新都会重新运行这部分代码。
- ✅ 适用:纯计算逻辑(如 const double = count * 2)。
- ❌ 禁忌:不要在这里写副作用(如 HTTP 请求、setState),因为在 React 的并发模式(Concurrent Mode)下,渲染阶段可能会被暂停、废弃或重复执行多次。
1 | |
3.2 useLayoutEffect (Commit Phase - Sync)
它的执行时机和类组件的 componentDidMount / componentDidUpdate 几乎一致。
- ✅ 适用:需要操作 DOM 且不希望用户看到中间状态的场景(如 tooltip 定位、动画起始位置计算)。
- ⚠️ 注意:它会阻塞浏览器绘制,使用过度会导致页面卡顿。
1 | |
3.3 useEffect (Commit Phase - Async)
这是 95% 的业务场景应该使用的 Hook。 它不会阻塞浏览器更新屏幕,能保证页面响应最快。
- ✅ 适用:数据获取、日志上报、建立 WebSocket、非视觉相关的 DOM 操作。
1 | |