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

相关推荐
原则猫4 小时前
UDP 到底怎么实现可靠的
前端
Java后端的Ai之路4 小时前
14、ReactJS - 路由(Router)
前端·react.js·状态模式
裕晟资质规划5 小时前
三类涉密资质延续时限差异分析:军工60日与集成印制3个月的边界
java·服务器·前端·网络·安全
ShineWinsu5 小时前
对于JavaScript:JavaScript基础语法的解析
开发语言·前端·javascript·面试·web·基础语法·循环
月夏5 小时前
Vue3 转 React:18 项对照表 + 5 个必踩的坑(附 7 天上手路线)
前端
广州华水科技6 小时前
单北斗GNSS位移监测系统的应用与维护解析
前端
梦曦i6 小时前
uni-storage 0.1.0 发布:TTL过期管理+多实例隔离
前端·storage·uni-app x
猫头_7 小时前
AI 流式传输工程指南:模型吐出半截 JSON,前端如何边收边渲染?
前端·javascript·llm
console.log('npc')7 小时前
第 1 章 项目概述与环境搭建
前端·人工智能·大模型
定栓7 小时前
Webpack迁到Vite第七节(二):全家桶移除 — Babel 全家桶深度分析
前端·webpack·node.js