0Pricing
React Academy · 课时

使用 useLayoutEffect 测量 DOM 元素

在布局完成但绘制前读取元素尺寸、位置和滚动偏移,避免闪烁

使用 useLayoutEffect 测量 DOM 元素 是 CoddyKit 上的免费 React Academy 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 React Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 React Academy 课程共包含 4 节课。

getBoundingClientRect 概览

element.getBoundingClientRect() 会返回一个 DOMRect 对象,其中包含元素相对于视口的大小和位置:top、right、bottom、left、width 和 height。为了返回准确值,它会强制浏览器执行布局计算,因此应减少调用次数。

在 useEffect 中读取的问题

如果您在 useEffect 中读取 getBoundingClientRect(),浏览器此时已经完成绘制。如果随后根据测量结果设置状态,React 会重新渲染,浏览器也会再次绘制。用户可能会先短暂看到位置或大小错误的元素,然后在第二次绘制时看到修正后的结果。

useLayoutEffect 如何解决这个问题

在 useLayoutEffect 中读取测量值发生在 DOM 变更之后、绘制之前。由测量触发的任何状态更新都会使 React 同步重新渲染并提交修正后的 DOM,整个过程都发生在浏览器进行任何绘制之前。用户只会在一次绘制周期中看到最终的正确结果。

工具提示定位

工具提示需要显示在其触发元素附近。请在 useLayoutEffect 中使用 getBoundingClientRect() 测量触发元素的位置,计算工具提示的绝对位置,并将该位置设置到状态中。工具提示会在第一次绘制时就显示在正确位置,不会从错误的初始位置发生可见跳动。

元素动画:读取初始尺寸

要让元素从自然(不受约束)的尺寸动画到固定尺寸,您需要在应用约束前知道它的自然尺寸。请在应用任何动画样式之前,于 useLayoutEffect 中读取元素的高度,将其存储到状态中,然后将其用作 CSS 过渡的关键帧起始值。

粘性页眉高度

当页面包含粘性页眉时,正文内容需要设置与页眉高度相等的顶部内边距,以免被页眉遮挡。请在 useLayoutEffect 中使用引用测量页眉高度:headerRef.current.getBoundingClientRect().height。将该值设置为内容容器的 paddingTop。这样从第一次渲染开始,布局就是正确的。

状态更新会导致同步重新渲染

当您在 useLayoutEffect 中调用 setState 时,React 会在同一个同步工作块中立即运行另一个渲染和提交周期。这次重新渲染也会运行其 useLayoutEffect。请注意不要造成无限循环:只有在新测量值与当前状态值不同时才更新状态。

SSR:测量结果为零

在服务器上,DOM 元素不存在,因此 getBoundingClientRect() 会抛出异常或返回零值。请使用检查来保护您的 useLayoutEffect:if (typeof window === 'undefined') return;。对于 SSR 应用,您可能需要先使用占位尺寸进行水合,然后在客户端水合完成后再进行测量。

使用 ResizeObserver 持续跟踪

使用 useLayoutEffect 进行一次性测量,只能得到元素在挂载时的尺寸。若要跟踪元素在调整大小时发生的尺寸变化(例如由于窗口大小调整、动态内容或 CSS 变更),请改用 ResizeObserver。它会在元素尺寸发生变化时触发回调,因此更适合响应式组件。

避免重复测量

如果多个副作用需要相同的测量结果,请计算一次,然后通过上下文共享,或将其作为属性向下传递。在多个 useLayoutEffect 调用中计算相同的 getBoundingClientRect() 值,会触发多次同步布局读取,而每次读取的成本都相对较高。请尽可能合并测量。

引用与 useLayoutEffect 的时序

引用会在副作用运行前完成赋值。当 useLayoutEffect 触发时,可以确保 ref.current 指向 DOM 元素。这就是该模式有效的原因:useLayoutEffect(() => { const rect = ref.current.getBoundingClientRect(); ... }, [])。对于已挂载的组件,在布局副作用中引用永远不会为 null。

getBoundingClientRect 的时序

为什么在 useLayoutEffect 中调用 getBoundingClientRect() 比在 useEffect 中调用更好?

课程回顾:测量 DOM 元素

请在 useLayoutEffect 中使用 getBoundingClientRect(),在变更后、绘制前读取 DOM 测量值。这些测量结果触发的状态更新会导致同步重新渲染,并纳入同一次绘制,从而防止闪烁。请使用 typeof window 检查来防护 SSR。若要持续跟踪尺寸,优先使用 ResizeObserver。

常见问题解答

「使用 useLayoutEffect 测量 DOM 元素」课时是免费的吗?

是的 — 「使用 useLayoutEffect 测量 DOM 元素」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 React Academy 课程的其余内容,请升级到 CoddyKit PRO。 React Academy 课程共包含 4 节课。

「使用 useLayoutEffect 测量 DOM 元素」这节课中我会学到什么?

在布局完成但绘制前读取元素尺寸、位置和滚动偏移,避免闪烁 你通过在浏览器中直接运行的动手代码来练习 React Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 React Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 React Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。

「使用 useLayoutEffect 测量 DOM 元素」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 React Academy 课中编写并运行代码吗?

能。每节 React Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 渲染阶段与 DOM 提交
  2. useLayoutEffect 与 useEffect 的运行时机
  3. 使用 useLayoutEffect 测量 DOM 元素
  4. 避免布局抖动与视觉闪烁
← 返回 React Academy