React 执行阶段与渲染机制详解(基于 React 18+ 官方文档)

React 执行阶段与渲染机制详解(基于 React 18+ 官方文档)

🌟 核心概念:React 的执行流程分层

React 的工作流程基于 Fiber 架构 ,将任务拆分为可中断的单元,核心分为 Render Phase(渲染阶段)Commit Phase(提交阶段) ,并辅以 副作用阶段卸载阶段。以下是详细解析:


一、Render Phase(渲染阶段 / 协调阶段)

1. 作用与触发

  • 目的:计算新的 Virtual DOM 树,并与旧树进行 Diff(协调算法),决定需要更新的部分。
  • 触发条件
    • 初始挂载(Mount)
    • 状态更新(State Update):useState/useReducer
    • Props 变化(父组件重渲染)
    • 强制更新(forceUpdate

2. 特性

  • 纯函数性 :组件函数必须是纯函数,禁止修改外部状态或变量 (如 propsstate、全局变量)。
  • 可中断性:React 18 的并发模式下,此阶段可被中断或重启,优先处理高优先级任务。
  • Strict Mode 行为 :开发环境下,React 会双调用组件函数,用于检测非纯函数导致的副作用。

3. 代码示例

jsx 复制代码
function Counter({ time }) {
  // 纯函数:相同输入返回相同 JSX
  return (
    <>
      <h1>{time}</h1>
      <input /> {/* 用户输入不会因父组件重渲染而丢失 */}
    </>
  );
}

二、Commit Phase(提交阶段)

1. 作用

将 Render Phase 计算出的变更同步应用到真实 DOM,确保用户界面即时更新。

2. 三个子阶段

  1. Before Mutation
    • 执行 getSnapshotBeforeUpdate(类组件)
    • 执行 useLayoutEffect清理函数
  2. Mutation
    • 实际插入、更新、删除 DOM 节点
  3. 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.memouseMemouseCallback 等工具。

2. 优化技巧

  • 减少不必要的 re-renders :使用 React.memo 包裹子组件。
  • 避免在 Render Phase 创建对象/函数 :使用 useMemo/useCallback 缓存。
  • 并发模式下的优先级调度: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 的并发能力提升应用性能。

相关推荐
唐某人丶2 小时前
教你如何用 JS 实现 Agent 系统(2)—— 开发 ReAct 版本的“深度搜索”
前端·人工智能·aigc
中微子2 小时前
深入剖析 useState产生的 setState的完整执行流程
前端
遂心_2 小时前
JavaScript 函数参数传递机制:一道经典面试题解析
前端·javascript
小徐_23332 小时前
uni-app vue3 也能使用 Echarts?Wot Starter 是这样做的!
前端·uni-app·echarts
RoyLin2 小时前
TypeScript设计模式:适配器模式
前端·后端·node.js
遂心_3 小时前
深入理解 React Hook:useEffect 完全指南
前端·javascript·react.js
Moonbit3 小时前
MoonBit 正式加入 WebAssembly Component Model 官方文档 !
前端·后端·编程语言
龙在天3 小时前
ts中的函数重载
前端
卓伊凡3 小时前
非常经典的Android开发问题-mipmap图标目录和drawable图标目录的区别和适用场景实战举例-优雅草卓伊凡
前端