用deepSeek彻底搞懂了useLayoutEffect和useEffect的区别

2,432 阅读2分钟

最近deepSeek大火,我也迫不及待的使用上了,体验确实很赞!

最近在准备面试,之前一直对 React 里面的 useLayoutEffectuseEffect 两个 hook 的区别不是很清楚,于是乎想到了deepSeek,我问了它这个问题,没想到它回答的超乎我的想象。我将它的回答做了一些整理分享出来

区别

useLayoutEffectuseEffectReact 中两个处理副作用的 Hook,它们的核心区别在于 执行时机对渲染流程的影响


1. 执行时机差异

useEffectuseLayoutEffect
触发时机在浏览器 绘制完成后 异步执行在 DOM 更新后、浏览器 绘制前 同步执行
阻塞渲染不会阻塞浏览器绘制会阻塞浏览器绘制直到回调完成

2. 底层原理对比

React 渲染流程(简化版):

  1. Render Phase(生成虚拟 DOM)
  2. Commit Phase(更新真实 DOM)
  3. Browser Paint(浏览器绘制页面)
  4. Effect Callbacks(执行 useEffect)
  • useLayoutEffect 的回调在 Commit Phase 后立即执行(步骤 2 和 3 之间)
  • useEffect 的回调在 Browser Paint 后执行(步骤 3 之后)

3. 使用场景对比

适合 useLayoutEffect 的场景:

  • 需要 同步修改 DOM(例如调整元素尺寸/位置)
  • 需要 在浏览器绘制前消除视觉闪烁(如快速切换状态导致的布局抖动)
  • 需要 读取 DOM 布局信息(如滚动位置、元素尺寸)

适合 useEffect 的场景:

  • 数据获取(API 请求)
  • 事件监听/取消监听
  • 非紧急的副作用操作(如日志记录)
  • 大多数常规副作用(React 官方推荐优先使用)

4. 代码示例对比

示例 1:修改 DOM 样式

// 使用 useEffect(可能看到闪烁)
function Example() {
  const ref = useRef();

  useEffect(() => {
    ref.current.style.marginLeft = '100px'; // 浏览器可能已经绘制过一次
  }, []);

  return <div ref={ref}>内容</div>;
}

// 使用 useLayoutEffect(无闪烁)
function Example() {
  const ref = useRef();

  useLayoutEffect(() => {
    ref.current.style.marginLeft = '100px'; // 在绘制前完成修改
  }, []);

  return <div ref={ref}>内容</div>;
}

示例 2:自动聚焦输入框

function AutoFocusInput() {
  const inputRef = useRef();

  useLayoutEffect(() => {
    inputRef.current.focus(); // 在绘制前完成聚焦
  }, []);

  return <input ref={inputRef} />;
}

5. 注意事项

  1. SSR 问题useLayoutEffect 在服务端渲染(SSR)时会导致警告

  2. 性能优化useLayoutEffect 的同步特性可能导致性能问题,应谨慎使用

  3. 执行顺序:同一组件中 useLayoutEffect 总是比 useEffect 先执行

总结表格

特性useEffectuseLayoutEffect
执行阶段异步(不阻塞渲染)同步(阻塞渲染)
最佳场景大多数副作用操作DOM 测量/同步修改
服务端渲染兼容性⚠️(需要条件判断)
React 官方推荐优先级优先使用特殊场景使用

当需要操作 DOM 且要求 视觉一致性 时使用 useLayoutEffect,其他情况优先使用 useEffect