讲一下useEffect和useLayoutEffect

useEffect 和 useLayoutEffect 都是 React 中的副作用 Hook ,它们的函数签名完全一样。唯一的、也是本质的区别在于执行时机不同。

简单来说:

  • useEffect :异步 执行,在浏览器绘制之后才运行。
  • useLayoutEffect :同步 执行,在浏览器绘制之前运行。

理解了这个时间差,就能明白它们各自的应用场景和潜在风险。


1. 核心区别:执行时机

为了直观地理解,可以看看下面这张图。它展示了从组件更新到屏幕呈现的完整流程:

sequenceDiagram participant 组件状态更新 participant React 更新 DOM participant 浏览器重新绘制屏幕 participant 执行副作用 组件状态更新->>React 更新 DOM: 触发渲染 Note over React 更新 DOM: React 计算出新的虚拟DOM<br/>并同步更新真实DOM React 更新 DOM->>浏览器重新绘制屏幕: DOM已更新,但屏幕还未更新 alt useLayoutEffect React 更新 DOM->>执行副作用: 立即同步执行(阻塞绘制) 执行副作用->>浏览器重新绘制屏幕: 副作用执行完成后才进行绘制 else useEffect React 更新 DOM->>浏览器重新绘制屏幕: 先进行屏幕绘制 浏览器重新绘制屏幕->>执行副作用: 异步执行,不阻塞绘制 end

总结:

  • useLayoutEffect 会阻塞浏览器绘制。副作用代码运行完后,用户才能看到屏幕上的最终画面。
  • useEffect 不会阻塞浏览器绘制。用户会先看到画面(可能是更新前的,或更新后但副作用还未生效的画面),然后副作用再悄悄运行。

2. 各自的使用场景

基于执行时机,它们各自有明确的适用场景。

Hook 执行时机 适用场景 风险
useEffect 浏览器绘制后 - 绝大部分副作用:数据获取、设置订阅、日志记录 - 操作不直接影响 用户看到的首屏视觉的DOM 可能产生视觉闪烁(如果副作用会明显改变DOM样式)
useLayoutEffect 浏览器绘制前 - 需要同步 读取或修改DOM(例如:获取元素尺寸、位置) - 需要在绘制前完成样式修改,避免视觉闪烁 - 阻塞绘制 ,可能降低性能 - 在服务端渲染(SSR)中会发出警告

useLayoutEffect 的典型代码示例

下面的例子展示了如何用 useLayoutEffect 解决闪烁问题。

jsx 复制代码
function MeasureAndFixComponent() {
  const ref = useRef();
  // 使用 useLayoutEffect 确保在浏览器绘制前,DOM 位置就被调整好
  useLayoutEffect(() => {
    const rect = ref.current.getBoundingClientRect();
    // 假设需要根据元素位置调整某个样式,比如让一个浮动提示框不超出屏幕
    if (rect.right > window.innerWidth) {
      ref.current.style.left = `${window.innerWidth - rect.width}px`;
    }
  }, []); // 依赖项为空,只在组件挂载时执行一次

  // 用户永远不会看到浮动提示框在错误位置闪烁一下,因为它已经被纠正了
  return <div ref={ref}>A tooltip or dropdown</div>;
}

如果用 useEffect 来实现上述逻辑,用户可能会先看到元素在原始位置闪现一下,然后瞬间跳转到正确位置,造成一种"闪烁"或"跳动"的不佳体验。


3. 常见误区

  • 误区一:为了避免SSR警告而放弃 useLayoutEffect

    • 澄清 :仅在 SSR 中会警告,在客户端渲染(CSR)中安全。对于 CSR 项目,完全可以使用 useLayoutEffect。若要消除 SSR 警告,官方推荐的做法是自定义一个 Hook,在客户端挂载前返回 useEffect,挂载后返回 useLayoutEffect。
  • 误区二:误用 useLayoutEffect 来替代 useEffect

    • 澄清 :大部分场景下 useEffect 是更优选择。滥用 useLayoutEffect 会强制 React 在绘制前同步执行代码,从而拖慢性能,让页面感觉卡顿。
  • 误区三:在 useLayoutEffect 里做异步操作

    • 澄清 :由于 useLayoutEffect 是同步执行的,如果在内部进行异步请求,会导致整个渲染过程被意外地拉长。数据和网络相关的副作用,始终应该放在 useEffect 中。

4. 总结(记忆要点)

  1. 执行顺序 :useLayoutEffect 先于 useEffect 执行。具体顺序是:useLayoutEffect 清理函数 -> useLayoutEffect 回调 -> useEffect 清理函数 -> useEffect 回调。

  2. 选择标准 :当你的副作用直接影响用户看到的画面 (例如需要精确测量或立即调整DOM),并且不希望出现抖动时,用 useLayoutEffect 。其他情况,默认都用 useEffect。

  3. 一句话总结 :useEffect 是异步的、非阻塞的,用于数据和大部分不直接改变视觉的副作用;useLayoutEffect 是同步的、阻塞的,用于在屏幕刷新前同步操作 DOM,避免视觉闪烁。

相关推荐
web打印社区1 分钟前
HTML5 静默打印:前端页面怎么调用打印机且尽量不弹窗
前端·javascript·vue.js·pdf·html·html5
Python大数据分析2 分钟前
开源免费、AI 驱动的 Web 打印设计器 OpenPrint:从拖拽设计到 ERP 对接全流程实战
前端·人工智能·开源
溪语流沙12 分钟前
【每天一个CSS | Day11】赛博朋克故障风文字:红青分离 + 切片抖动
前端·css
ShineWinsu27 分钟前
对于Redis:List类型的解析
数据库·c++·redis·链表·缓存·面试·list
liangshanbo121536 分钟前
面试题:AI 对话上下文超限怎么办?
面试·webpack·devops
liangshanbo12151 小时前
React Fiber:从“解决卡顿”到完整更新链路
前端·javascript·react.js
Timmy1 小时前
前端转全栈笔记:讲框架之前,先把 TypeScript 这关过了
前端·人工智能·node.js
计算机魔术师1 小时前
年收入650亿、亏420亿,Anthropic凭什么敢叫价万亿?
前端
Frag0ut1 小时前
Chrome Canary 最新实验功能:AI Mode、按设备筛选历史记录与 Gemini 自动改密
前端·人工智能·chrome·dev·beta·canary·最新实验功能
江华森1 小时前
大模型应用技术-Prompt工程实践 — 学习笔记
面试