必须为resize事件加防抖,推荐用16ms settimeout结合requestidlecallback兜底;监听元素尺寸应使用resizeobserver;移动端横竖屏需以matchmedia为主、orientationchange为辅。

直接用 window.addEventListener('resize', handler) 就能监听窗口大小变化,但不加防抖会高频触发、卡顿甚至崩溃——尤其在拖拽调整窗口时,Chrome 每秒可能触发上百次。
为什么 resize 事件容易出问题
浏览器对 resize 的触发没有节制:只要布局重排(reflow)发生,就可能触发;用户缓慢拖动窗口时,函数可能连续执行几十次,而你真正需要的只是“最终稳定后的尺寸”。document.documentElement.clientWidth 和 window.innerWidth 返回值还可能因滚动条存在与否产生 17px 差异,不统一取值会导致布局跳变。
必须加防抖,且推荐用 requestIdleCallback 兜底
单纯 setTimeout 防抖(如 200ms)在低端设备上仍可能堆积任务。更稳妥的做法是结合 requestIdleCallback 延迟到空闲帧执行:
let resizeTimer;
const handleResize = () => {
clearTimeout(resizeTimer);
resizeTimer = setTimeout(() => {
const w = document.documentElement.clientWidth;
const h = document.documentElement.clientHeight;
// 执行布局适配、Canvas重绘等耗时操作
}, 16);
};
window.addEventListener('resize', handleResize);
// 首次加载也要调用一次
handleResize();
-
16ms对齐一帧时长,比250ms更及时,又避免过度响应 - 不要用
debounce工具函数封装后直接传入addEventListener,容易丢失this或闭包引用 - 如果页面有大量图表/Canvas,建议在
handleResize内部用cancelAnimationFrame清掉上一次未完成的重绘
想监听某个 div 而不是整个窗口?别用 window.resize
window.resize 只响应浏览器窗口变化,对 CSS 动态修改、Flex 分配、max-width 生效等导致的元素尺寸变更完全无感。此时必须换 ResizeObserver:
const ro = new ResizeObserver(entries => {
for (const entry of entries) {
const { width, height } = entry.contentRect;
console.log('div 尺寸变了:', width, height);
// 注意:contentRect 不含 padding/border,需手动加 getComputedStyle
}
});
ro.observe(document.querySelector('#my-chart'));
- IE 完全不支持,需引入
resize-observer-polyfill(注意它无法监听display: none切换) -
entry.contentRect是只读对象,不要试图修改它 - 观察多个元素时,复用同一个
ResizeObserver实例,不要为每个元素新建一个
移动端横竖屏切换不能只靠 resize
iOS Safari 在键盘弹出、地址栏收起时也会触发 resize,但这时 window.innerHeight 变化不代表视口真实可用高度。更可靠的是监听 orientationchange 事件并配合 window.matchMedia:
window.addEventListener('orientationchange', () => {
setTimeout(() => {
const isLandscape = window.matchMedia('(orientation: landscape)').matches;
console.log('当前方向:', isLandscape ? '横屏' : '竖屏');
}, 100);
});
注意:Safari 中 orientationchange 触发时机晚于 resize,且部分 iPadOS 版本已弃用该事件,所以必须用 matchMedia 主判断。
真正麻烦的不是监听本身,而是每次尺寸变化后你要重算什么:是重设 Canvas 的 width/height 属性?还是仅改 CSS transform: scale()?前者清空绘图上下文,后者不触发重排但可能模糊。这些决策比监听逻辑重得多。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











