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


📚 延伸阅读

相关推荐
东风破_1 小时前
TypeScript 高级类型进阶:keyof、Exclude、Record 与类型组合思想
前端·后端·typescript
DS随心转插件2 小时前
Grok生成的html怎么导出——AI导出鸭:大模型结构化输出的“最后一公里”工程化解构
前端·人工智能·ai·html·豆包·deepseek·ai导出鸭
এ慕ོ冬℘゜2 小时前
使用 jQuery 动态渲染表格与状态切换
前端·javascript·jquery
人间凡尔赛2 小时前
2026 前端必修:4 个让 CSS 脱胎换骨的现代新特性(附实战代码)
前端·css
用户2181697049303 小时前
Flutter (二十) 轮播图
前端
前端snow3 小时前
ai agent - RAG 汇总
前端
计算机魔术师3 小时前
AI为拿高分不惜入侵网站:22个模型作弊审计,真相让人背脊发凉
前端
晓说前端3 小时前
TypeScript 核心语法应用 —— Vue 3 中的使用(下)
前端·javascript·typescript·类型系统
zhangminghuan4 小时前
微信小程序开发核心知识点全景解析(前端必备硬核基础)
前端·微信小程序·小程序
boooooooom4 小时前
尝尝咸淡:从朴素 RAG 到 Graph RAG——一个烹饪问答系统的三层检索升级之路
前端·后端·llm