被 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 是 "急着搞定"。记住它们干活的时间点,以后遇到样式闪烁的坑,就知道该找谁帮忙啦~ 你们平时用这俩钩子的时候,还遇到过啥有意思的问题?评论区交流下呗~

相关推荐
pe7er3 小时前
window管理开发环境篇 - 持续更新
前端·后端
We་ct4 小时前
LeetCode 5. 最长回文子串:DP + 中心扩展
前端·javascript·算法·leetcode·typescript
陈随易8 小时前
有生之年系列,Nodejs进程管理pm2 v7.0发布
前端·后端·程序员
冰暮流星8 小时前
javascript之事件代理/事件委托
前端
陈随易9 小时前
AI时代,你还在坚持手搓文章吗
前端·后端·程序员
里欧跑得慢11 小时前
17. Flutter Hero动画实现:让界面过渡更加优雅
前端·css·flutter·web
IT_陈寒12 小时前
Vue的这个响应式陷阱,我debug了一整天才爬出来
前端·人工智能·后端
cn_mengbei12 小时前
用React Native开发OpenHarmony应用:Reanimated共享元素过渡
javascript·react native·react.js
kyriewen12 小时前
前端测试:别为了100%覆盖率而写测试,那是自欺欺人
前端·javascript·单元测试
去伪存真12 小时前
我自己写的第一个skills--project-core-standards
前端·agent