ResizeObserver 必须用 new 调用,回调接收 contentRect(不含 padding/border/margin),仅响应布局变化;需检查元素存在性与 isConnected;transform 不影响 contentRect;须手动 unobserve 防内存泄漏。

ResizeObserver 构造函数必须用 new 调用
直接调用 ResizeObserver() 会报 TypeError: Illegal constructor。它不是普通函数,而是 Web API 的类,必须配合 new 使用。
常见错误写法:
const ro = ResizeObserver(() => {}); // ❌ 报错正确写法:
const ro = new ResizeObserver(entries => {
for (const entry of entries) {
console.log(entry.contentRect.width, entry.contentRect.height);
}
});- 构造时传入的回调函数会在尺寸变化后异步触发,参数是
ResizeObserverEntry[]数组 - 每个
entry的contentRect是只读对象,包含width/height/top/left等,对应元素 content box 尺寸(不含 padding/border/margin) - 不要在回调里同步调用
ro.observe()或ro.unobserve(),可能引发循环观察
observe() 必须传入一个已挂载的 DOM 元素
ro.observe() 接收的是真实节点,不是选择器字符串或 NodeList。传入 null、undefined 或未插入文档的元素都会静默失败(不报错,但不会响应变化)。
立即学习“前端免费学习笔记(深入)”;
典型误用场景:
文章转信息图。将文章/笔记转化为手机可读的 HTML 信息图,自动匹配视觉风格。触发场景:文章转图、笔记转图、信息图、转小红书图、做张图、可视化这篇文章、文生图。
-
ro.observe(document.querySelector('#missing'))—— 元素不存在时返回null,监听无效 -
ro.observe(document.querySelectorAll('.box'))——querySelectorAll返回 NodeList,不是单个 Element - 在
document.createElement('div')后立刻observe(),但该元素还没 append 到 body
稳妥做法是加一层存在性检查:
const el = document.getElementById('target');
if (el && el.isConnected) {
ro.observe(el);
}contentRect 不反映 CSS transform 缩放
ResizeObserver 监听的是布局尺寸变化,不是渲染后视觉尺寸。如果元素仅靠 transform: scale(1.5) 放大,contentRect.width 不会变 —— 它仍报告原始 layout width。
需要视觉尺寸时,得自己算:
const style = getComputedStyle(el);
const scaleX = parseFloat(style.transform.split(',')[0].split('(')[1]) || 1;
const visualWidth = entry.contentRect.width * scaleX;-
contentRect受box-sizing影响:设为border-box时,width包含 padding 和 border;content-box(默认)则只含 content - Flex/Grid 子项尺寸变化(如父容器伸缩导致子项重排)能被正常捕获
- 字体加载、图片加载引起的回流也会触发,但文字内容增减(不改变盒模型)不会
需手动 unobserve 避免内存泄漏
ResizeObserver 实例不会自动释放对目标元素的引用。如果元素被移除,但没调用 ro.unobserve(el),该元素无法被 GC,尤其在 SPA 中反复挂载/卸载组件时容易累积。
建议配对使用:
// 观察前 ro.observe(el); <p>// 卸载前(如 React useEffect cleanup / Vue onBeforeUnmount) ro.unobserve(el);</p><p>// 或彻底停用 ro.disconnect(); // 停止所有观察,实例可复用但需重新 observe
-
disconnect()不会清空内部观察列表,后续还能再observe() - 不要在回调中反复
unobserve+observe来“刷新”,除非你明确需要重置观察起点(比如想忽略初始化尺寸) - 现代框架中,推荐把
ResizeObserver实例和组件生命周期绑定,避免跨组件共享同一实例引发状态混乱
真正麻烦的不是怎么写,而是判断「这次尺寸变更是由什么触发的」—— 浏览器 resize、CSS 类切换、JS 修改 width、甚至 font load 都可能混在一起。别依赖单一 contentRect 差值做业务逻辑,最好结合 MutationObserver 或自定义 flag 做归因。


















