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


📚 延伸阅读

相关推荐
PineappleCoder10 小时前
还在重复下载资源?HTTP 缓存让二次访问 “零请求”,用户体验翻倍
前端·性能优化
拉不动的猪10 小时前
webpack编译中为什么不建议load替换ast中节点删除consolg.log
前端·javascript·webpack
李姆斯10 小时前
Agent时代下,ToB前端的UI和交互会往哪走?
前端·agent·交互设计
源码获取_wx:Fegn089511 小时前
基于springboot + vue健身房管理系统
java·开发语言·前端·vue.js·spring boot·后端·spring
闲谈共视11 小时前
基于去中心化社交与AI智能服务的Web钱包商业开发的可行性
前端·人工智能·去中心化·区块链
CreasyChan11 小时前
C# 反射详解
开发语言·前端·windows·unity·c#·游戏开发
JIngJaneIL11 小时前
基于Java+ vue智慧医药系统(源码+数据库+文档)
java·开发语言·前端·数据库·vue.js·spring boot
hashiqimiya13 小时前
两个步骤,打包war,tomcat使用war包
java·服务器·前端
零度@13 小时前
Java中Map的多种用法
java·前端·python
yuanyxh14 小时前
静默打印程序实现
前端·react.js·electron