useEffect 与 useLayoutEffect 有什么区别?

818 阅读3分钟

useEffectuseLayoutEffect 是 React 中用于处理副作用的两个 Hook,它们的主要区别在于执行时机使用场景。理解它们的区别对于优化性能和避免 UI 问题非常重要。


1. 共同点

  • 两者都用于在函数组件中执行副作用操作(如数据获取、DOM 操作、订阅等)。
  • 两者的 API 完全相同,接收两个参数:
    • 一个副作用函数。
    • 一个依赖项数组(可选)。

2. 区别

1. 执行时机

  • useEffect

    • 副作用函数在浏览器完成渲染之后异步执行
    • 不会阻塞浏览器的渲染过程。
    • 适合大多数副作用操作,尤其是那些不需要立即更新 DOM 的场景。
  • useLayoutEffect

    • 副作用函数在浏览器完成渲染之前同步执行
    • 会阻塞浏览器的渲染过程,直到副作用函数执行完毕。
    • 适合需要同步更新 DOM 的场景,例如在渲染之前测量 DOM 元素或更新布局。

2. 使用场景

  • useEffect

    • 数据获取(如调用 API)。
    • 订阅事件。
    • 不需要立即更新 DOM 的操作。
  • useLayoutEffect

    • 需要同步更新 DOM 的操作(如调整元素尺寸或位置)。
    • 在渲染之前测量 DOM 元素。
    • 避免 UI 闪烁(例如,在渲染之前更新样式)。

3. 执行顺序

  1. 组件渲染。
  2. useLayoutEffect 的副作用函数同步执行。
  3. 浏览器绘制 DOM。
  4. useEffect 的副作用函数异步执行。

4. 代码示例

以下代码演示了 useEffectuseLayoutEffect 的执行顺序:

import React, { useEffect, useLayoutEffect, useState } from 'react';

function App() {
  const [value, setValue] = useState(0);

  useEffect(() => {
    console.log('useEffect - 异步执行');
  }, [value]);

  useLayoutEffect(() => {
    console.log('useLayoutEffect - 同步执行');
  }, [value]);

  return (
    <div>
      <p>{value}</p>
      <button onClick={() => setValue(value + 1)}>增加</button>
    </div>
  );
}

export default App;

输出结果:

  1. useLayoutEffect - 同步执行
  2. useEffect - 异步执行

5. 使用场景示例

useEffect 示例:数据获取

useEffect(() => {
  fetch('/api/data')
    .then(response => response.json())
    .then(data => setData(data));
}, []);

useLayoutEffect 示例:同步更新 DOM

useLayoutEffect(() => {
  const element = document.getElementById('my-element');
  if (element) {
    element.style.width = '100px'; // 同步更新 DOM
  }
}, []);

6. 注意事项

  1. 性能影响

    • useLayoutEffect 是同步执行的,可能会阻塞浏览器的渲染,导致性能问题。除非必要,否则应优先使用 useEffect
  2. 服务端渲染(SSR)

    • 在服务端渲染时,useLayoutEffect 不会执行,因为此时没有 DOM。如果需要在 SSR 中使用,可以考虑使用 useEffect 或在 useLayoutEffect 中添加条件判断。
  3. 避免 UI 闪烁

    • 如果某些操作(如更新样式)在 useEffect 中执行会导致 UI 闪烁,可以尝试将其移到 useLayoutEffect 中。

7. 总结

特性useEffectuseLayoutEffect
执行时机浏览器渲染之后异步执行浏览器渲染之前同步执行
是否阻塞渲染
使用场景数据获取、订阅事件等同步更新 DOM、测量 DOM 元素等
性能影响较小较大(可能阻塞渲染)
服务端渲染支持支持不支持
  • 优先使用 useEffect,除非需要在渲染之前同步更新 DOM。
  • 在需要避免 UI 闪烁或同步操作 DOM 时,使用 useLayoutEffect