序言
在 React 的舞台上,useEffect
和 useLayoutEffect
如同两位配合默契的后台助手,共同确保演出完美呈现。但你知道他们何时上场,如何协作吗?
舞台上的两位助手
想象你正在导演一场精彩的舞台剧:
- 🎭 演员 = React 组件
- 💡 舞台 = 浏览器屏幕
- 🧙 后台助手1号 =
useEffect
- 🧙 后台助手2号 =
useLayoutEffect
助手1号:useEffect - 幕后的完美主义者
useEffect(() => {
// 演出结束后才工作
console.log('演出结束,我来打扫后台')
}, [])
助手2号:useLayoutEffect - 幕间的闪电侠
useLayoutEffect(() => {
// 演出间隙快速调整道具
console.log('演出间隙,我调整了舞台灯光')
}, [])
关键区别:演出时间表
时刻 | useEffect | useLayoutEffect | 观众感受 |
---|---|---|---|
演员准备阶段 | ❌ 不参与 | ❌ 不参与 | - |
演员表演阶段 | ❌ 不参与 | ❌ 不参与 | 观看表演 |
表演结束,幕布未落 | ❌ 等待 | ✅ 立即工作 | 看到闪烁 |
幕布落下后 | ✅ 开始工作 | ❌ 已完成 | 看不到变化 |
防闪烁魔法:useLayoutEffect 的绝技
想象这个场景:弹窗需要居中显示
问题代码(使用 useEffect):
function Modal() {
const ref = useRef()
useEffect(() => {
// 在渲染后调整位置
const height = ref.current.offsetHeight
ref.current.style.marginTop = `${(window.innerHeight - height) / 2}px`
}, [])
return <div ref={ref}>弹窗内容</div>
}
观众体验:
- 看到弹窗出现在左上角(初始位置)
- 瞬间跳到屏幕中央(闪烁)
解决方案(使用 useLayoutEffect):
function Modal() {
const ref = useRef()
useLayoutEffect(() => {
// 在渲染前调整位置
const height = ref.current.offsetHeight
ref.current.style.marginTop = `${(window.innerHeight - height) / 2}px`
}, [])
return <div ref={ref}>弹窗内容</div>
}
观众体验:
- 直接看到居中显示的弹窗(无闪烁)
真实案例:避免内容跳动
动态调整元素高度
function DynamicBox() {
const ref = useRef()
const [content, setContent] = useState('短文本')
useLayoutEffect(() => {
// 同步更新高度
ref.current.style.height = '200px'
}, [content])
return (
<div
ref={ref}
style={{ background: 'lightblue' }}
onClick={() => setContent('很长很长的文本...')}
>
{content}
</div>
)
}
效果对比:
- 使用
useEffect
:点击后先看到短框,然后突然变高(闪烁) - 使用
useLayoutEffect
:高度变化瞬间完成(无闪烁)
双助手协作的艺术
最佳配合方案
useLayoutEffect(() => {
// 1. 紧急任务:需要立即执行的DOM操作
element.style.position = 'absolute'
element.style.top = '50px'
// 2. 测量布局
const rect = element.getBoundingClientRect()
return () => {
// 清理布局副作用
}
}, [dependencies])
useEffect(() => {
// 1. 数据获取
fetchData().then(setData)
// 2. 事件订阅
window.addEventListener('resize', handleResize)
return () => {
// 清理数据副作用
window.removeEventListener('resize', handleResize)
}
}, [dependencies])
性能权衡:何时用谁?
场景 | 推荐助手 | 原因 |
---|---|---|
DOM 测量/调整 | useLayoutEffect | 避免布局闪烁 |
数据获取 | useEffect | 不阻塞渲染 |
事件订阅 | useEffect | 非紧急任务 |
动画起始 | useLayoutEffect | 确保起始状态正确 |
第三方库集成 | useEffect | 通常不需要同步执行 |
新手指南:黄金法则
-
默认首选 useEffect
- 适用于大多数场景
- 不会阻塞页面渲染
- 更符合React的异步特性
-
遇到闪烁问题时考虑 useLayoutEffect
- DOM布局调整
- 元素位置计算
- 样式同步更新
-
清理工作同样重要
useLayoutEffect(() => { // 设置DOM属性 element.style.color = 'red' return () => { // 恢复原始状态 element.style.color = '' } }, [])
幕后原理揭秘
React 更新流程:
-
useLayoutEffect 阶段:
- DOM已更新,但浏览器还未绘制
- 同步执行,会阻塞浏览器绘制
-
useEffect 阶段:
- 浏览器已完成绘制
- 异步执行,不阻塞页面
总结:完美舞台的秘诀
在 React 的舞台上:
-
useEffect 是幕后的清洁工和联络员
- 演出结束后工作
- 处理数据、订阅等"后台事务"
-
useLayoutEffect 是幕间的道具师
- 演出间隙快速调整
- 解决视觉闪烁问题
- 处理DOM布局相关操作
记住这个黄金法则:
当你在屏幕上看到内容后才需要调整 - 用 useEffect
当你需要在用户看到前完成调整 - 用 useLayoutEffect
掌握这两位助手的协作艺术,你就能打造出流畅无闪烁的 React 应用!