React Hook 钩子 useInsertionEffect、useLayoutEffect、useEffect的区别

要搞懂 useInsertionEffectuseLayoutEffectuseEffect 的区别,核心是抓住 执行时机适用场景 ------ 这三个 Hook 都是 React 处理"副作用"的工具,但执行阶段、阻塞特性、适用场景完全不同,选对了能避免渲染闪烁、样式失效等问题。

先看一张核心对比表,快速建立认知:

特性 useEffect useLayoutEffect useInsertionEffect
执行时机 组件渲染完成(屏幕绘制后) 组件渲染完成(屏幕绘制前) DOM 生成后、布局计算前(React 18+ 新增)
是否阻塞渲染 不阻塞(异步) 阻塞(同步) 阻塞(同步,更早)
能否操作 DOM 能(但可能导致闪烁) 能(无闪烁) 能(仅用于插入样式)
触发时机排序 最晚 中间 最早
核心用途 数据请求、事件监听、异步操作 DOM 测量、同步修改 DOM、避免闪烁 CSS-in-JS 插入样式、避免样式计算重复

一、逐个拆解:执行时机 + 核心用法

1. useEffect(最常用)
  • 执行时机 :组件首次渲染/依赖更新后 → 浏览器完成屏幕绘制 → 执行 useEffect 回调(异步,不阻塞页面显示)。

  • 核心特点:和浏览器"绘制"解耦,是 React 推荐的默认副作用 Hook。

  • 适用场景

    • 数据请求(接口调用)、异步操作;
    • 订阅/取消订阅(如事件监听、WebSocket);
    • 不依赖 DOM 布局的操作(如修改状态、记录日志)。
  • 示例

    js 复制代码
    import { useEffect, useState } from 'react';
    
    function UserList() {
      const [users, setUsers] = useState([]);
    
      // 组件渲染完成后请求数据(不阻塞页面)
      useEffect(() => {
        const fetchUsers = async () => {
          const res = await fetch('/api/users');
          setUsers(await res.json());
        };
        fetchUsers();
    
        // 清理副作用(组件卸载时取消监听)
        return () => {
          // 比如取消定时器、移除事件监听
        };
      }, []); // 空依赖:仅首次渲染执行
    
      return <div>{users.map(u => <p key={u.id}>{u.name}</p>)}</div>;
    }
2. useLayoutEffect(同步操作 DOM)
  • 执行时机 :组件首次渲染/依赖更新后 → React 完成 DOM 变更 → 浏览器未绘制屏幕 → 同步执行 useLayoutEffect 回调 → 执行完成后浏览器才绘制屏幕(阻塞绘制)。

  • 核心特点 :比 useEffect 早执行,同步阻塞渲染,能避免 DOM 操作导致的"视觉闪烁"。

  • 适用场景

    • DOM 测量(如获取元素宽高、位置)并同步修改 DOM;
    • 避免闪烁的样式修改(比如根据元素位置调整弹窗位置);
    • 依赖 DOM 布局的同步操作。
  • 示例(解决测量 DOM 导致的闪烁)

    js 复制代码
    import { useLayoutEffect, useRef, useState } from 'react';
    
    function Popup() {
      const [width, setWidth] = useState(0);
      const ref = useRef(null);
    
      // useLayoutEffect:绘制前测量,无闪烁
      useLayoutEffect(() => {
        // 同步获取 DOM 宽度(此时 DOM 已生成,未绘制)
        const w = ref.current.offsetWidth;
        setWidth(w); // 修改状态后,React 会在绘制前重新计算布局
      }, []);
    
      // 若用 useEffect:绘制后测量,会先显示 0,再更新为实际宽度(闪烁)
      // useEffect(() => { ... }, []);
    
      return <div ref={ref}>弹窗宽度:{width}px</div>;
    }
3. useInsertionEffect(React 18+ 新增,专用于样式插入)
  • 执行时机 :React 生成 DOM 节点后 → useLayoutEffect 执行前 → 浏览器计算布局/绘制前(是三个 Hook 中最早执行的)。

  • 核心特点 :专为 CSS-in-JS 库设计,解决"样式插入时机晚于布局计算"导致的性能问题,只能用于插入样式,不能修改状态(修改状态会触发额外渲染)。

  • 适用场景

    • CSS-in-JS 库插入动态样式(如 styled-components、emotion);
    • 需在布局计算前插入样式,避免浏览器重复计算布局。
  • 示例(CSS-in-JS 插入样式)

    js 复制代码
    import { useInsertionEffect } from 'react';
    
    // 自定义 CSS-in-JS 样式插入 Hook
    function useInjectStyle(style) {
      useInsertionEffect(() => {
        // 布局计算前插入样式,避免重复计算
        const styleTag = document.createElement('style');
        styleTag.innerHTML = style;
        document.head.appendChild(styleTag);
    
        return () => {
          document.head.removeChild(styleTag);
        };
      }, [style]);
    }
    
    function StyledButton() {
      // 插入动态样式
      useInjectStyle(`
        .btn {
          background: blue;
          color: white;
          padding: 8px 16px;
        }
      `);
    
      return <button className="btn">自定义样式按钮</button>;
    }

二、关键执行顺序(实战必懂)

以组件首次渲染为例,完整执行流程:

复制代码
1. React 渲染组件 → 生成 DOM 节点(未布局、未绘制)
2. 执行 useInsertionEffect 回调(仅插入样式,不修改状态)
3. 浏览器计算 DOM 布局(宽高、位置)
4. 执行 useLayoutEffect 回调(同步修改 DOM/状态,可能触发重新布局)
5. 浏览器绘制屏幕(页面显示)
6. 执行 useEffect 回调(异步,不阻塞)

三、避坑指南(新手常错)

1. 不要滥用 useLayoutEffect

useLayoutEffect 阻塞绘制,若回调内逻辑复杂(如大量计算),会导致页面加载变慢。能用 useEffect 解决的,就不用 useLayoutEffect

2. useInsertionEffect 不能修改状态

useInsertionEffect 执行时,React 还未完成"布局提交",修改状态会触发额外的布局计算,导致性能问题 ------ 它的唯一用途是插入样式。

3. 依赖项的坑

三个 Hook 的依赖项规则一致:

  • 空数组 []:仅首次渲染执行;
  • 依赖变量:变量变化时执行;
  • 无依赖:每次渲染都执行。
4. 服务端渲染(SSR)注意
  • useInsertionEffect 是唯一能在 SSR 中安全插入样式的 Hook(useLayoutEffect 在 SSR 中会报警告);

  • 若用 useLayoutEffect 做 SSR,需加判断:

    js 复制代码
    const isClient = typeof window !== 'undefined';
    useLayoutEffect(() => {
      if (isClient) {
        // 仅客户端执行 DOM 操作
      }
    }, []);

总结

  1. 选 useEffect:绝大多数场景(数据请求、异步操作、事件监听),默认首选;
  2. 选 useLayoutEffect:需要同步操作 DOM、测量布局、避免视觉闪烁时;
  3. 选 useInsertionEffect:React 18+ 中做 CSS-in-JS 样式插入,或需在布局计算前插入样式时。

核心记忆点:

  • 执行顺序:useInsertionEffectuseLayoutEffectuseEffect
  • 阻塞性:前两个阻塞渲染,useEffect 不阻塞;
  • 专用性:useInsertionEffect 仅用于样式插入,不要挪作他用。

如果有具体场景(比如"修改样式导致闪烁""CSS-in-JS 样式不生效"),可以贴出代码,我帮你选对应的 Hook 并优化。

相关推荐
kyriewen1 小时前
我把今年流传的前端 AI 面试题整理了一遍——4 类场景题+回答框架(附速查表)
前端·面试·程序员
计算机魔术师1 小时前
国产多模态模型正面硬刚Opus旗舰:差距从30%缩到3%
前端
风骏时光牛马2 小时前
程序员进阶:深度思考,解锁职场成长的底层逻辑
前端
IT_陈寒2 小时前
用了Proxy才发现以前的JavaScript白写了
前端·人工智能·后端
爱丶不疚2 小时前
Eval: Agent 说的 Eval 是什么?从单测、TDD 到 Sentry 聊起
前端·ai编程·vibecoding
求道於盲2 小时前
python中的类型标注
前端
计算机魔术师2 小时前
从硅谷测试到全球铺开,ChatGPT广告的10亿美元秘密
前端
专业抄代码选手4 小时前
08|Fiber 上的 `useState`:状态终于属于具体组件
前端·javascript·react.js
默_笙4 小时前
😭 Vibe Coding 翻车实录:AI 编程为什么必须先写"剧本"
前端·javascript
LEE4 小时前
原来 Claude Code 最厉害的工具,一行代码都不写
前端·后端