React Hook 解析(二):`useEffect` 与 `useLayoutEffect`

在 React 的函数组件中,副作用的处理离不开两个核心 Hook:useEffectuseLayoutEffect。虽然它们看起来相似,但在执行时机、使用场景和对渲染的影响方面存在显著差异。


🧠 什么是副作用(Side Effect)

副作用是指任何与组件渲染无关、且可能影响外部世界的操作,例如:

  • DOM 操作

  • 订阅 / 清除订阅

  • 请求接口

  • 设置定时器

  • 日志打印

React 为此提供了两个 Hook:useEffectuseLayoutEffect


🔁 useEffect:渲染后执行

🔧 执行时机:

useEffect浏览器完成绘制之后 异步执行。这意味着:

useEffect 总是在屏幕更新后执行,不会阻塞浏览器渲染。

📌 语法:

scss 复制代码
useEffect(() => {   
	// 副作用逻辑   
	return () => {// 可选:清理函数 }; 
	},[deps]
);

🧪 示例:

javascript 复制代码
useEffect(() => {   
	console.log('组件挂载或依赖变化后执行');    
	return () => { console.log('组件卸载或依赖变化前清理'); }; 
}, [value]);

✅ 适合场景:

  • 请求数据(fetch)

  • 添加订阅 / 事件监听

  • 设置定时器

  • 修改状态

  • 本地存储操作(localStorage)


⚡️ useLayoutEffect:DOM 更新前执行

🔧 执行时机:

useLayoutEffectDOM 变更后、浏览器绘制前 同步执行。也就是说:

它会阻塞浏览器绘制,确保你能在用户看到页面之前同步更新 DOM。

📌 语法相同:

scss 复制代码
useLayoutEffect(() => {   
	// 同步副作用   
	return () => { // 清理操作 }; 
}, [deps]);

🧪 示例:

ini 复制代码
useLayoutEffect(() => {   
	const width = ref.current?.offsetWidth;   
	console.log('在绘制前读取 DOM 宽度:', width); 
}, []);

✅ 适合场景:

  • 读取布局信息(如宽高、位置)

  • 手动触发布局变更(如 scroll、focus)

  • 与动画库协同使用(如 GSAP)


⏱ 执行时机对比图解

rust 复制代码
render --> commit --> paint              
               ↑       ↑      
    useLayoutEffect useEffect
  • useLayoutEffect:在 commit 后立即执行(同步),阻塞绘制

  • useEffect:在 paint 后异步执行,不会阻塞绘制


🚨 性能建议

React 官方建议:

优先使用 useEffect,除非你必须在绘制前操作 DOM,才使用 useLayoutEffect

原因是:

  • useLayoutEffect 可能阻塞页面渲染,影响首屏性能;

  • 在服务端渲染(SSR)中,useLayoutEffect 会发出警告(推荐降级为 useEffect);


🎯 实战比较:防止闪烁 vs 非阻塞渲染

1️⃣ 使用 useEffect(有可能先绘制再调整):

ini 复制代码
useEffect(() => {   
	boxRef.current.style.transform = 'translateX(100px)'; 
}, []);

🔍 结果:初始会看到盒子在左边,然后才跳过去。


2️⃣ 使用 useLayoutEffect(绘制前调整):

ini 复制代码
useLayoutEffect(() => {   
	boxRef.current.style.transform = 'translateX(100px)'; 
}, []);

🔍 结果:页面一开始就看到正确的位置,没有跳动。


✅ 总结对比

特性 useEffect useLayoutEffect
执行时机 浏览器绘制后(异步) DOM 变更后,绘制前(同步)
是否阻塞绘制
适合操作 网络请求、日志、订阅 DOM 尺寸、同步动画、滚动位置
SSR 支持 ⚠️ 警告(不推荐)
推荐使用 默认选择 仅限于必须同步操作的特殊情况

🧩 开发建议

  1. ✅ 优先使用 useEffect

  2. ⚠️ 只有在你必须读取 DOM 布局或阻止闪烁 时,才使用 useLayoutEffect

  3. 💡 如果你要做动画,优先考虑 CSS 或 requestAnimationFrame,再退而求其次使用 useLayoutEffect


📚 延伸阅读

相关推荐
带娃的IT创业者12 小时前
TypeScript + React + Ant Design 前端架构入门:搭建一个 Flask 个人博客前端
前端·react.js·typescript
非凡ghost13 小时前
MPC-BE视频播放器(强大视频播放器) 中文绿色版
前端·windows·音视频·软件需求
Stanford_110613 小时前
React前端框架有哪些?
前端·微信小程序·前端框架·微信公众平台·twitter·微信开放平台
洛可可白13 小时前
把 Vue2 项目“黑盒”嵌进 Vue3:qiankun 微前端实战笔记
前端·vue.js·笔记
学习同学14 小时前
从0到1制作一个go语言游戏服务器(二)web服务搭建
服务器·前端·golang
-D调定义之崽崽14 小时前
【初学】调试 MCP Server
前端·mcp
四月_h15 小时前
vue2动态实现多Y轴echarts图表,及节点点击事件
前端·javascript·vue.js·echarts
文心快码BaiduComate15 小时前
用Zulu轻松搭建国庆旅行4行诗网站
前端·javascript·后端
行者..................16 小时前
手动编译 OpenCV 4.1.0 源码,生成 ARM64 动态库 (.so),然后在 Petalinux 中打包使用。
前端·webpack·node.js
小爱同学_17 小时前
一次面试让我重新认识了 Cursor
前端·面试·程序员