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


📚 延伸阅读

相关推荐
SoaringHeart6 分钟前
Flutter小技巧:IM音浪效果实现
前端·flutter
小old弟7 分钟前
亲测autojs自动化,关闭应用的三种方法
前端
AndyLaw7 分钟前
我用 ChatGPT 起手、脚本改造,给孩子做了一个绘本
前端·javascript·openai
放空欧巴7 分钟前
学习 elpis 有感 -- 初识 elpis-core (实现简易版 Egg.js)
前端
前端开发呀9 分钟前
震惊!开启浏览器翻译竟会导致react应用报错?
前端·react.js
Sun_light11 分钟前
从 0 到 1 实现低代码编辑器的基本功能
前端·react.js·typescript
WildBlue12 分钟前
从 0 到 1 上手 React 中的 mitt,前端小白也能秒懂!🤓
前端·react.js·前端框架
星河那美18 分钟前
使用vis-timeline 完成时间轴事件追踪表
前端·vue.js
前端小棒槌18 分钟前
前端项目同时配置ESlint和Prettier
前端