Back

ResizeObserver 与 Window Resize:何时使用哪个

ResizeObserver 与 Window Resize:何时使用哪个

你正在构建一个图表组件,需要在其容器缩小时重新绘制。你使用 window.addEventListener('resize', ...),它大部分时候都能工作——直到面板因侧边栏折叠而调整大小时,却没有任何事件触发。这就是本文要解决的核心问题。

核心要点

  • window resize 事件仅在浏览器视口改变大小时触发。它对单个 DOM 元素一无所知。
  • ResizeObserver 监视特定元素,并在其尺寸发生变化时触发,无论变化原因是什么。
  • 在视口调整大小后轮询元素尺寸是脆弱的——它会错过由 CSS 切换、动态内容、字体加载等引起的变化。
  • 对于不需要 JavaScript 的基于容器的样式设置,CSS 容器查询是首选工具。

每个 API 实际观察什么

window resize 事件浏览器视口改变大小时触发。这就是它的全部作用范围。它对单个 DOM 元素一无所知。

ResizeObserver 监视特定元素,并在其尺寸发生变化时触发——无论是什么原因导致的。视口调整大小、DOM 插入、CSS 类切换、父级 flexbox 重排——如果元素的大小发生变化,所有这些都会触发观察器。

这是根本区别。它们观察不同的东西,通常用于不同的场景。

何时使用 Window Resize 事件

当你的逻辑真正依赖于视口尺寸,而不是任何特定元素的大小时,window resize 事件是正确的工具:

  • 在视口断点处切换移动导航布局
  • 将全页面 canvas 或 WebGL 上下文调整为 window.innerWidth / window.innerHeight
  • 调整依赖于屏幕空间的全局布局状态
window.addEventListener('resize', () => {
  canvas.width = window.innerWidth
  canvas.height = window.innerHeight
  render()
})

对于纯视觉的、由 CSS 驱动的对视口大小的响应,完全优先使用媒体查询而不是 JavaScript。

何时使用 ResizeObserver

ResizeObserver 的使用场景集中在必须响应元素实际渲染大小的组件级行为:

  • 当容器宽度变化时重新计算坐标轴比例的图表
  • 根据可用空间调整工具栏布局的文本编辑器
  • 可调整大小的面板或分割窗格小部件
  • 任何不控制自己容器的嵌入式小部件
const ro = new ResizeObserver((entries) => {
  for (const entry of entries) {
    const { inlineSize, blockSize } = entry.contentBoxSize[0]
    chart.resize(inlineSize, blockSize)
  }
})

ro.observe(document.querySelector('.chart-container'))

理解现代 API 形式

当前的 ResizeObserver API 在每个条目上暴露 contentBoxSizeborderBoxSize 数组——每个都包含 inlineSize(水平书写模式下的宽度)和 blockSize(高度)。较旧的 contentRect 属性仍然有效,但主要是为了向后兼容。在新代码中优先使用 contentBoxSize[0]

为什么不在 Window Resize 后轮询?

一个常见的模式——监听 window resize 事件,然后对元素调用 getBoundingClientRect()——是脆弱的。它会错过与视口无关的大小变化:动态内容加载、CSS 类切换、注入的子元素、字体加载。ResizeObserver 能捕获所有这些。视口调整大小后的轮询则不能。

性能和反馈循环

ResizeObserver 回调在布局之后、绘制之前运行,这意味着回调内的 DOM 读取反映当前布局,而不会强制重排。这在结构上比在 resize 事件处理程序内读取布局属性更安全。

一个注意事项:如果你在观察元素自己的回调内修改其大小,会触发另一次观察。ResizeObserver 通过在同一帧内以逐渐更深的 DOM 深度处理观察来处理这个问题,如果循环无法解决,最终会报告错误——防止无限循环。你仍然应该谨慎对待在回调内写入的内容。

当你的回调涉及布局或元素大小变化独立于视口调整大小发生时,ResizeObserver 的性能优势变得有意义。

CSS 容器查询替代方案

如果你的目标是根据容器的大小设置元素样式,CSS 容器查询是正确的工具——不需要 JavaScript。将 ResizeObserver 保留给运行时逻辑(而非样式)必须响应大小变化的情况。

场景最佳工具
视口范围的布局变化window resize 事件
元素尺寸驱动 JS 逻辑ResizeObserver
仅基于容器的样式设置CSS 容器查询

结论

当你关心视口时使用 window resize 事件。当你关心元素时使用 ResizeObserver。当你只需要改变样式时使用 CSS 容器查询。这三个工具覆盖了响应式行为的完整空间——选择与你实际测量内容匹配的那个。

常见问题

ResizeObserver 在元素尺寸变化时触发,因此将元素折叠为零宽度或零高度会触发它。但是,它不会因保持尺寸不变的可见性或不透明度变化而触发。对于真正的可见性检测,请改用 IntersectionObserver。

一旦元素被垃圾回收,观察器会自动停止跟踪它。但是,显式调用 unobserve 或 disconnect 是良好实践,特别是在具有挂载和卸载生命周期的单页应用程序或框架组件中,以避免过时引用和意外回调。

是的。ResizeObserver 在 Chrome、Firefox、Safari 和 Edge 中都有广泛支持。如果需要支持较旧的浏览器,可以使用 polyfill,但它在内部依赖轮询和 mutation observers,因此无法匹配原生性能。

对于轻量级 UI 更新通常不需要。ResizeObserver 已经在渲染管道内高效地批处理通知。但是,如果你的回调触发昂贵的下游工作,如网络请求或繁重计算,应对该特定工作进行防抖,而不是整个回调。

Complete picture for complete understanding

Capture every clue your frontend is leaving so you can instantly get to the root cause of any issue with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.

Check our GitHub repo and join the thousands of developers in our community.

OpenReplay