几乎每个前端开发者都写过这样的代码:给 window 挂上 resize 事件,在回调里读取容器的 getBoundingClientRect().width,然后重新布局。这招在用户拖拽浏览器窗口时确实管用。可一旦容器尺寸因为其他原因改变——父级 flex 布局被兄弟内容撑开、容器查询断点触发 CSS 变动、内部图片加载完成引发重排、你用脚本动态增删子节点——window resize 就毫无反应了。窗口缩放只是元素尺寸变化的众多诱因之一,而 ResizeObserver 要来补上剩下的那部分。

它的调用方式并不复杂。你创建一个 ResizeObserver 实例,传入回调,再告诉它要观察哪个元素。只要这个元素的大小发生变化,无论起因是什么,回调就会被触发。想停掉对某个元素的观察,调用 unobserve;想一次性断开所有观察,调用 disconnect。一个观察者可以同时盯住多个元素,浏览器还可能把连续的尺寸突变合并成一次通知,所以回调收到的参数始终是一个数组,你必须遍历所有条目。

打开网易新闻 查看精彩图片

每个条目提供了三种读取新尺寸的途径。第一种是较早的 contentRect 属性,它只报告内容区域。第二种是 contentBoxSize,采用逻辑尺寸命名——inlineSize 对应水平书写模式下的宽度,blockSize 对应高度——这样在竖排文字等垂直书写模式下代码也无需改动。第三种是 borderBoxSize,返回值包含内边距和边框,效果相当于 offsetWidth,却避免了一次主动的布局读取。对于常规横向布局,contentBoxSize[0].inlineSize 就是内容宽度,而 borderBoxSize[0].inlineSize 则给出带内外边距和边框的总宽。

一个很实在的应用场景出现在 canvas 上。当 canvas 的容器尺寸改变时,如果还想保持绘制内容的像素完美,就得同步更新画布本身的宽高属性。传统 window resize 监听在这里同样有盲区,而 ResizeObserver 可以精确捕获容器变化,拿到最新的逻辑尺寸后,把它同时赋给 canvas 的 width 和 height,再按新的像素密度重绘。这样一来,无论是侧边栏展开、网格调整还是内容动态注入,画布始终不会被拉伸模糊。

从 window 到 ResizeObserver,监听口径从“整个窗口”收缩到“单个元素”,却覆盖了此前大量被忽略的尺寸变化来源。这种精细化的尺寸通知正在成为现代响应式布局和组件自适应逻辑的标配能力,也让“实时、精准地跟随容器”终于不再是靠 hack 才能做到的事。