React的执行规则是啥

60 阅读5分钟

React 的执行规则主要围绕其核心机制:声明式 UI 渲染虚拟 DOM 差分算法组件生命周期 和 状态驱动更新。以下是 React 执行流程的关键规则和原理的详细说明:


1. 虚拟 DOM 与 Diff 算法

  • 虚拟 DOM:React 通过 JavaScript 对象构建虚拟 DOM,与真实 DOM 解耦,减少直接操作 DOM 的性能开销。
  • Diff 算法:当组件状态或 props 变化时,React 会重新生成虚拟 DOM,并通过 Diff 算法对比新旧虚拟 DOM 的差异,最终将差异高效地应用到真实 DOM 上(最小化操作)。

2. 组件执行流程

React 的执行分为两个阶段:渲染阶段(Reconciliation)  和 提交阶段(Commit)

渲染阶段(Reconciliation)

  • 组件渲染:React 递归调用组件的 render 方法(函数组件返回 JSX,类组件调用 render()),生成新的虚拟 DOM。
  • Fiber 架构:React 16 引入 Fiber 架构,将渲染工作拆分为小任务,支持中断和恢复(Concurrent Mode 的基础)。
  • 副作用标记:在渲染阶段,React 会标记需要执行的副作用(如 DOM 操作、数据获取)。

提交阶段(Commit)

  • 应用更新:React 将渲染阶段的结果一次性应用到真实 DOM。
  • 执行副作用:调用生命周期方法(如 useEffectcomponentDidMount)和事件回调。

3. 状态更新与异步性

  • 不可变性原则:状态更新必须通过 setState 或 useState,不可直接修改状态。

  • 异步批处理:React 会将多个状态更新合并为一次渲染(例如在事件处理或生命周期方法中),以提高性能。

    javascript
    // 示例:多次 setState 会被合并
    this.setState({ count: this.state.count + 1 });
    this.setState({ count: this.state.count + 1 }); // 最终只触发一次渲染
    
  • useEffect 的延迟执行useEffect 在组件渲染完成后(提交阶段)执行,可能被延迟(尤其在 Concurrent Mode 下)。


4. 生命周期方法(类组件)

复制

阶段生命周期方法说明
挂载constructor → render → componentDidMount组件首次渲染完成
更新render → componentDidUpdate状态/props 变化后触发
卸载componentWillUnmount组件销毁前清理资源

5. 函数组件与 Hooks

  • Hooks 执行顺序:Hooks 必须在函数组件顶层按顺序调用(React 通过规则校验)。

  • useEffect:在每次渲染后执行,但可通过依赖项数组控制是否跳过。

    javascript
    useEffect(() => {
      // 每次 count 变化时执行
    }, [count]);
    
  • useMemo/useCallback:惰性计算和缓存函数/值,避免重复渲染时的性能浪费。


6. Context 与 Provider

  • Context 更新:当 Provider 的 value 变化时,所有订阅该 Context 的子组件会重新渲染(即使未直接使用 value)。
  • 避免过度渲染:使用 React.memo 或 useMemo 优化子组件性能。

7. 错误边界(Error Boundaries)

  • 捕获错误:类组件可通过 componentDidCatch 捕获子组件中的错误,防止整个应用崩溃。
  • 函数组件限制:函数组件无法直接使用错误边界,需包裹在类组件中。

8. 并发模式(Concurrent Mode)

  • 可中断渲染:React 18 引入的并发模式允许中断低优先级任务(如页面加载),优先处理高优先级任务(如用户输入)。

  • 新 API

    • useTransition:标记非紧急状态更新,允许用户界面保持响应。
    • useDeferredValue:延迟更新非关键 UI 内容。

9. 性能优化规则

  • 避免不必要的渲染

    • 使用 React.memo(函数组件)或 PureComponent(类组件)进行浅比较。
    • 合理使用 useMemo 和 useCallback 缓存计算结果。
  • Key 的重要性:列表渲染时为每个元素指定稳定的 key,帮助 React 正确识别变化。

  • 懒加载组件:使用 React.lazy 和 Suspense 动态加载组件。


10. 执行顺序示例

以一个简单组件为例:

javascript
function Counter() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    document.title = `Count: ${count}`;
  }, [count]);

  return (
    <button onClick={() => setCount(count + 1)}>
      Count: {count}
    </button>
  );
}
  1. 初始渲染:调用 useState(0) 初始化 count,渲染按钮。
  2. 点击事件:触发 setCount(count + 1),React 将状态更新加入队列。
  3. 重新渲染:React 重新调用 Counter,生成新的虚拟 DOM。
  4. 提交阶段:更新 DOM 显示新 count,执行 useEffect(更新页面标题)。

总结

React 的执行规则围绕 声明式更新 和 高效渲染 设计,核心逻辑是:

  1. 状态驱动视图:状态变化 → 重新渲染 → 虚拟 DOM 对比 → 更新真实 DOM。
  2. 异步与批处理:状态更新可能被合并,副作用延迟执行。
  3. 性能优化:通过缓存、避免重复渲染和并发模式提升性能。

理解这些规则后,可以更高效地开发 React 应用并解决常见问题(如无限循环更新、性能瓶颈)。

最后,总结一下React的基础执行规则

  1. 只在最顶层使用 Hook:不要在循环(for)、条件(if)或嵌套函数中调用 Hook,确保总是在你的 React 函数的 最顶层调用他们。遵守这条规则,就能确保 Hook 在每一次渲染中都按照同样的顺序被调用,这让 React 能够在多次的 useState 和 useEffect 调用之间保持 hook 状态的正确。
  2. 只在 React 组件或者其他自定义Hook函数中调用:不要在普通的 JavaScript 函数中调用 Hook。可以在 React的函数组件中调用 Hook,也可以在自定义 Hook 中调用其他 Hook。遵循此规则,可以确保组件的状态逻辑在代码中清晰可见。

以上就是 React Hooks 的基本执行规则。它们都是为了确保Hooks 的正确和一致的行,帮助开发者更好地管理和控制组件的状态和副作用。