无布局重计算场景下ResizeObserver与window resize事件监听器的效率对比及验证方法
咱们先从你给出的示例说起——你写的这段代码里,ResizeObserver和window的resize事件回调执行次数、触发时机看起来完全一致,甚至Chrome 130 DevTools的输出也印证了这一点(除了ResizeObserver初始化时会多跑一次,这个细节你也注意到了)。那在这种没有布局读写操作、不会触发布局抖动的简单场景下,两者到底有没有效率差异呢?
核心结论
在你这种纯日志输出(或者无布局操作)的回调场景下,两者的性能差异几乎可以忽略不计,甚至可以认为是完全等价的。
为什么会这样?
ResizeObserver被推崇为更高效的方案,核心是针对普通DOM元素(比如<div>)的尺寸监听场景:
- 当你监听普通DOM元素时,window的resize事件会在窗口变化时无脑触发,哪怕目标元素的尺寸根本没改变;但ResizeObserver只会在目标元素的尺寸真的发生变化时才触发回调,能避免大量冗余执行。
- 之前关于“ResizeObserver比EventTarget更高效”的讨论,也都是针对普通DOM元素的——因为给普通元素绑定resize事件(还要处理冒泡等逻辑)的开销,远大于ResizeObserver的精准尺寸跟踪。
但你现在监听的是document.documentElement(也就是HTML根元素),它的尺寸变化和window的尺寸变化是强绑定的:窗口一 resize,根元素的尺寸必然跟着变,反之亦然。这种情况下,ResizeObserver和window resize事件的触发逻辑本质上是对齐的,自然也就没了效率优势。
怎么用代码验证?
如果你想亲手验证两者的效率,可以借助浏览器的performance API,或者DevTools的Performance面板:
1. 统计单次回调的执行耗时
let e = 0, r = 0; new ResizeObserver(() => { const start = performance.now(); console.log('resized from observer', r++); console.log('Observer 单次回调耗时:', performance.now() - start); }).observe(document.documentElement); window.addEventListener('resize', () => { const start = performance.now(); console.log('resized from event', e++); console.log('Resize 事件单次回调耗时:', performance.now() - start); });
快速拖动窗口resize,你会看到两者的单次回调耗时几乎完全一致,都是微秒级的差距,完全可以忽略。
2. 统计批量触发的总开销
如果想测试大量触发时的整体表现,可以加个计时结束的逻辑:
let eCount = 0, rCount = 0; let eTotalTime = 0, rTotalTime = 0; let testEnded = false; // 监听ResizeObserver new ResizeObserver(() => { if (testEnded) return; const start = performance.now(); // 这里替换成你的无布局操作(比如只是计数) rCount++; rTotalTime += performance.now() - start; }).observe(document.documentElement); // 监听window resize事件 window.addEventListener('resize', () => { if (testEnded) return; const start = performance.now(); // 同样的无布局操作 eCount++; eTotalTime += performance.now() - start; }); // 3秒后结束测试,打印统计结果 setTimeout(() => { testEnded = true; console.log(`ResizeObserver 总回调次数: ${rCount},总耗时: ${rTotalTime.toFixed(2)}ms`); console.log(`Window resize 总回调次数: ${eCount},总耗时: ${eTotalTime.toFixed(2)}ms`); console.log(`单次回调平均耗时 - Observer: ${(rTotalTime/rCount).toFixed(4)}ms,Event: ${(eTotalTime/eCount).toFixed(4)}ms`); }, 3000);
快速拖动窗口3秒后,你会发现两者的总耗时、平均单次耗时几乎没有区别。
3. 用Chrome DevTools可视化验证
打开Chrome DevTools的「Performance」面板,点击录制按钮后快速拖动窗口resize,录制结束后查看:
- 你会看到两者的回调函数在主线程上的占用时间几乎重合
- 没有任何额外的布局、重绘开销(因为你的回调里没有布局读写)
- 整体主线程占用率也没有明显差异
最后总结
在你这种无布局重计算的场景下,ResizeObserver和window resize事件监听器的效率是完全等价的。ResizeObserver的优势只体现在监听普通DOM元素尺寸变化的场景中,当监听根元素时,它和window resize事件的表现就没什么区别了。
备注:内容来源于stack exchange,提问作者Gaurang Tandon

