resizeobserver 只监听元素 content-box 的宽高变化,如 flex/grid 重排、内容撑开等;不监听 transform、opacity、padding/border 变更、display: none、伪元素及未挂载 shadow dom;需确保元素已挂载,避免无限循环和内存泄漏。

ResizeObserver 能监听什么,不能监听什么
它只响应元素 content-box 的宽高变化,也就是 layout 阶段真实发生的尺寸变更。能捕获的包括:flex 分配后宽度收缩、grid 项重排、内容插入撑开高度、visibility: hidden → visible 后回流、max-width 生效导致截断等。
但以下情况它完全不触发:transform: scale()(纯渲染层缩放)、opacity、filter、padding 或 border 变更(不改变 content area)、display: none(元素已脱离文档流)、伪元素和未挂载的 Shadow DOM 节点。
常见误判是:给一个空 div 加了 padding: 50px,指望 contentRect.height 变成 100 —— 实际仍是 0,因为没内容,content-box 高度为 0。
初始化和 observe() 必须确保元素已挂载
ResizeObserver 不会报错告诉你元素不存在,而是静默失败。传入 null、undefined 或尚未 append 到 DOM 的节点,回调永远不会执行。
- 原生 JS:放在
DOMContentLoaded回调里,或用document.getElementById('target')拿到后再调用ro.observe(el) - React:写在
useEffect(() => { ro.observe(el); return () => ro.unobserve(el); }, [])中,确保el是有效 ref - Vue:在
mounted()钩子中执行,避免 SSR 渲染时document未定义 - 动态插入节点(如
innerHTML或appendChild)后,需等插入完成再observe()
漏掉这一步,控制台不会报错,但你的监听逻辑就“凭空消失”了。
回调里改样式极易引发无限循环
在 ResizeObserver 回调中直接设置 target.style.width、target.style.padding 或 target.style.display,会再次触发 layout 计算,进而再次进入回调 —— 浏览器卡死或崩溃不是危言耸听。
安全做法是:
- 只读取
entry.contentRect.width和entry.contentRect.height,用于更新状态、类名或 CSS 自定义属性(如el.style.setProperty('--width', width + 'px')) - 如需调整尺寸,改兄弟元素或父容器,避开
entry.target自身 - 必须同步修改时,加防抖或用
requestAnimationFrame推迟到下一帧 - 调试时可加 guard:
if (width !== lastWidth) { /* update */; lastWidth = width; },但只是临时手段
组件卸载时必须 unobserve 或 disconnect
React/Vue 组件销毁后,若没调用 ro.unobserve(el) 或 ro.disconnect(),ResizeObserver 实例仍持有对 DOM 节点的强引用,且回调可能继续执行 —— 此时 entry.target 可能为 null 或已销毁,极易抛出 Cannot read property 'xxx' of null。
尤其要注意:
- 不要在回调里反复调用
ro.observe()或ro.unobserve(),可能漏触发或引发竞态 - 一个
ResizeObserver实例可观察多个元素,但所有回调共用同一队列,避免在回调中再操作 observer 本身 - SSR 场景下,整个初始化逻辑必须包裹在
if (typeof window !== 'undefined')中,否则服务端报错
真正容易被忽略的不是“怎么监听”,而是“什么时候停止监听”——没清理的 observer 是典型的内存泄漏源头。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











