ResizeObserver是专为监听单个DOM元素尺寸变化设计的原生API,它异步执行、性能友好、精准响应content-box变化,避免了window.onresize无法感知非窗口触发的尺寸变更及轮询导致的重排开销。

ResizeObserver 不是“监听窗口变化”的替代品,而是专为监听单个元素尺寸变化设计的原生 API;直接用它监听容器,比轮询或 window.resize + 手动计算更精准、性能更好、逻辑更干净。
为什么不能只靠 window.onresize?
窗口大小没变,但元素自己变了——比如拖拽侧边栏、动态插入内容、字体加载完成、CSS 动画结束,这些都会导致元素 offsetWidth 或 offsetHeight 改变,但 window.onresize 完全不会触发。
- 强行用
setInterval轮询getBoundingClientRect()会频繁触发重排(reflow),尤其在低端设备上明显卡顿 - 监听
mousemove或dragend是 hack,漏掉非交互引起的尺寸变化(如异步内容渲染后撑开容器) - 原生
ResizeObserver的回调在浏览器布局之后、绘制之前异步执行,天然避开强制同步布局
ResizeObserver 实例创建和观察目标的写法
核心就三行,但有三个关键点必须注意:
- 回调函数必须是稳定引用(避免每次 render 都新建匿名函数,否则
observe()会失效) -
entry.contentRect是推荐读取的尺寸来源,它比target.offsetWidth更可靠(包含 padding、border,且不受 CSS transform 影响) - 一个
ResizeObserver实例可观察多个元素,但所有被观察元素共享同一个回调,需用entry.target区分
示例:
const ro = new ResizeObserver((entries) => {
for (const entry of entries) {
const { width, height } = entry.contentRect;
console.log(`${entry.target.id} 尺寸变为 ${width}×${height}`);
}
});
ro.observe(document.getElementById('chart-container'));
ro.observe(document.getElementById('sidebar'));
React/Vue 组件中如何安全使用?别忘了清理
组件卸载时若不调用 disconnect(),观察者仍驻留在内存里,可能引发内存泄漏或对已销毁 DOM 触发错误回调。
- React 中应在
useEffect的 cleanup 函数里调用ro.disconnect() - Vue 2 在
beforeDestroy,Vue 3 在onBeforeUnmount中断开 - 不要在每次渲染都新建
ResizeObserver实例——它本身轻量,但频繁创建+销毁反而增加 GC 压力
React 示例片段:
useEffect(() => {
const ro = new ResizeObserver(handleResize);
ro.observe(ref.current);
return () => ro.disconnect();
}, []);
兼容性兜底:什么时候需要 resize-observer-polyfill?
原生 ResizeObserver 在 IE 全系列、Safari < 13.1、部分旧版 Android WebView 中不可用。是否引入 polyfill 取决于你的用户环境:
- 若项目明确不支持 IE 且最低要求 Safari 13.1+,可直接用原生 API,无需 polyfill
- 若需兼容 Safari 12 或更低版本,
resize-observer-polyfill会降级为基于scroll+animationframe的模拟方案,但注意:它无法检测纯 CSS 引起的尺寸变化(如font-size切换未触发重排) - polyfill 的节流逻辑在
src/utils/throttle.js,默认 60fps,不可配置;原生 API 则由浏览器调度,更优
简单检测写法:
if (typeof ResizeObserver === 'undefined') {
// 加载 polyfill
import('resize-observer-polyfill').then(({ default: ResizeObserver }) => {
window.ResizeObserver = ResizeObserver;
});
}
真正容易被忽略的是:ResizeObserver 不监听子元素内容变化本身,只监听「最终渲染出的尺寸」变化。如果父容器设置了 overflow: hidden,而子元素高度增长但被裁剪,此时父容器尺寸不变,observer 就不会触发——这不是 bug,是设计使然。


















