被 50px 到 200px 的闪烁整破防了?useLayoutEffect 和 useEffect 的区别原来在这

写了个简单的布局:一个浅蓝色块默认高 50px,加载后要改成 200px 并居中。结果每次刷新都先看到矮块闪一下,再变成高块 ------ 明明就两行样式,咋就出了这种幺蛾子?

排查半天发现,罪魁祸首是我用错了钩子。今天就借着这个真实案例,聊聊 useEffect 和 useLayoutEffect 到底咋区分,以及为啥换个钩子就能解决闪烁问题。

先搞懂俩钩子到底啥时候干活?

不管是 useEffect 还是 useLayoutEffect,本质都是用来处理 "副作用" 的 ------ 比如操作 DOM、发请求、订阅事件这些。但它们干活的时间点,差了关键一步。

先简单说下 useEffect:它就像个 "慢性子",等页面渲染完、浏览器把内容画到屏幕上之后,才慢悠悠地执行。比如你改个 DOM 样式,它会等页面已经显示出来了再动手,所以有时候会看到 "先这样,再那样" 的跳变。

而 useLayoutEffect 是个 "急性子",它会在 DOM 更新完但还没画到屏幕上的时候就冲上去干活。相当于说:"等一下!先别画!让我改完这处再显示!" 所以它能保证你改的样式在屏幕上一次性到位,不会出现中间状态。

为啥会有 "闪烁"?用个案例说清楚

最典型的场景就是处理 DOM 样式的时候。比如你想让一个元素从 A 样式瞬间变成 B 样式,用不好就会闪一下。

我做了个简单的例子:

jsx 复制代码
import { 
  useEffect,
  useLayoutEffect,
  useRef
 } from 'react'

function Show(){
   const ref = useRef(null)
  useEffect(()=>{
    const heightPx = 200; // 明确使用数值
    ref.current.style.height = `${heightPx}px`;
    ref.current.style.marginTop = `${(window.innerHeight - heightPx) / 2}px`;
  },[])

   return (
    <div ref={ref} style={{height:'50px',background:'lightblue'}}>
      内容
    </div>
   )
}

当你在浏览器快速刷新页面的时候,会看到明显的残影,也就是所谓的'闪烁'

换成 useLayoutEffect 试试:

jsx 复制代码
// 阻塞渲染 同步的感觉
useLayoutEffect(()=>{
    const heightPx = 200; // 明确使用数值
    ref.current.style.height = `${heightPx}px`;
    ref.current.style.marginTop = `${(window.innerHeight - heightPx) / 2}px`;
},[])

这次无论怎么刷新,都不会出现'闪烁效果'!因为 useLayoutEffect 在浏览器把内容画到屏幕之前就改完了样式,相当于 "偷偷" 改好再展示,自然不会有中间状态。

核心区别就一句话

  • useEffect:渲染完 → 浏览器画到屏幕上 → 再执行(异步,不阻塞渲染)
  • useLayoutEffect:渲染完 → 改 DOM → 浏览器再画(同步,会阻塞渲染)

简单说,useLayoutEffect 是 "赶在显示前插队改样式",useEffect 是 "显示完了再慢慢改"。

啥时候用哪个?

大多数时候用 useEffect 就行,毕竟不阻塞渲染,性能更好。但如果遇到这些情况,就得请 useLayoutEffect 出场了:

  1. 操作 DOM 样式时出现闪烁(比如上面的例子)
  2. 需要 "同步" 拿到 DOM 更新后的状态(比如获取元素宽高后立即调整位置)

不过要注意,useLayoutEffect 里别写太耗时的代码,不然会卡住页面 ------ 它可是会等你执行完才让浏览器画画的。

最后再总结下:useEffect 是 "佛系处理",useLayoutEffect 是 "急着搞定"。记住它们干活的时间点,以后遇到样式闪烁的坑,就知道该找谁帮忙啦~ 你们平时用这俩钩子的时候,还遇到过啥有意思的问题?评论区交流下呗~

相关推荐
光影少年1 小时前
react批量更新、同步/异步更新场景
前端·react.js·掘金·金石计划
假如让我当三天老蒯1 小时前
模块化:ES Module 与 CommonJS 的区别
前端·面试
用户40950115773171 小时前
Private Forge v2.0 发布:12大前端业务场景技能系统
前端
YFF菲菲兔2 小时前
completeRoot 源码解析
react.js
weedsfly2 小时前
异步编程全景与事件循环——彻底搞懂 JS 执行机制
前端·javascript
用户059540174462 小时前
AI Agent记忆测试踩坑实录:Mock骗了我一周,Mem0+pytest一招破局
前端·css
用户1733598075372 小时前
纯前端 PDF 数字签名实战:Vue 3 + pdf-lib 在浏览器里完成签名嵌入
前端·javascript
IT_陈寒3 小时前
SpringBoot自动配置的坑,我爬了三天才出来
前端·人工智能·后端
Avan_菜菜10 小时前
AI 能写代码了,为什么我反而开始要求它先写文档?
前端·github·ai编程
爱勇宝14 小时前
鸿蒙生态的下半场:开发者不只要能开发,还要能赚钱
android·前端·程序员