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 并优化。

相关推荐
摸鱼仙人~几秒前
Vue中markdown-it基础使用教程
前端·javascript·vue.js
弓.长.7 分钟前
ReactNative for OpenHarmony项目鸿蒙化三方库:lottie-react-native — Lottie动画组件
react native·react.js·harmonyos
落魄江湖行8 分钟前
入门篇二:Nuxt 4路由自动生成:告别手动配置路由的日子
前端·vue.js·typescript·nuxt4
CQU_JIAKE1 小时前
4.4【Q】
java·前端·javascript
小陈工1 小时前
Python Web开发入门(十二):使用Flask-RESTful构建API——让后端开发更优雅
开发语言·前端·python·安全·oracle·flask·restful
木斯佳1 小时前
前端八股文面经大全:字节前端一面(2026-04-03)·面经深度解析
前端·面试题·面经
xiaotao1311 小时前
第八章:实战项目案例
前端·vue.js·vite·前端打包
GISer_Jing1 小时前
Electron 全场景调试实战指南
javascript·electron·状态模式
We་ct1 小时前
JS手撕:性能优化、渲染技巧与定时器实现
开发语言·前端·javascript·面试·性能优化·定时器·性能
taWSw5OjU1 小时前
vue对接海康摄像头-H5player
开发语言·前端·javascript