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

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


🧠 什么是副作用(Side Effect)

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

  • DOM 操作

  • 订阅 / 清除订阅

  • 请求接口

  • 设置定时器

  • 日志打印

React 为此提供了两个 Hook:useEffect 和 useLayoutEffect。


🔁 useEffect:渲染后执行

🔧 执行时机:

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

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

📌 语法:

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

🧪 示例:

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

✅ 适合场景:

  • 请求数据(fetch)

  • 添加订阅 / 事件监听

  • 设置定时器

  • 修改状态

  • 本地存储操作(localStorage)


⚡️ useLayoutEffect:DOM 更新前执行

🔧 执行时机:

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

它会阻塞浏览器绘制,确保你能在用户看到页面之前同步更新 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


📚 延伸阅读

相关推荐
子兮曰2 小时前
把 649 种文件格式塞进一个网页里:一个「文件不出浏览器」的预览器
前端·javascript·后端
sm_926787055 小时前
RFID 标签打印的技术实现要点与二次开发实践
java·大数据·前端·c++·编辑器
IMPYLH5 小时前
HTML 的 <video> 元素
前端·html
weixin_448119946 小时前
Datawhale Easy Data × AI:构建知识与记忆驱动的 Agent笔记8
人工智能·笔记·react.js
溪语流沙6 小时前
【每天一个CSS | Day19】流光文字与一笔写成的手写签名
前端·javascript·css
逐光者9336 小时前
STM32——SPI 屏 · Flash · 高速
java·前端·stm32
To_OC6 小时前
同样是让 AI 写全栈,为什么别人一句话就够了
前端·前端框架·next.js
CDwenhuohuo6 小时前
electron pc项目打包成桌面端
前端·javascript·electron
前端大斗师6 小时前
「大于 1000」把 1000 元那单也算进去了:我在 Vue3 订单页对了 10 句话
前端·人工智能·typescript·大模型·原力计划
Jmyd01237 小时前
用 Web VR 引擎搭建 3D 虚拟展厅:从素材上云到多端实时渲染的完整数据链路
前端·3d·vr·三维数字化