在 QQ 浏览器最新版(内核 Chromium 123.0.6312.124)中,监听 window resize 事件时,发现在没有手动拖拽窗口的情况下,切换页面路由或操作 DOM 时,resize 事件被意外触发。
第一步:初步怀疑滚动条
resize 事件触发时,clientWidth 变化有时是 10px,接近滚动条宽度,初步怀疑是 DOM 切换导致滚动条出现/消失,挤压了页面宽度。
但有时变化只有 1px,且关闭侧边栏后问题完全消失,滚动条假说无法解释全部现象。
第二步:扩大观测范围
在 resize 回调中同时打印多个宽度指标:
观测结果:
关键发现:innerWidth 也在变化。
innerWidth 是视口宽度,不受页面内容、滚动条影响。它发生变化意味着问题根源在浏览器 UI 层,而非页面本身。
第三步:排除 sub-pixel 取整
怀疑侧边栏导致视口宽度为非整数,布局取整产生 1px 误差。执行验证:
两者均为整数,排除 sub-pixel 取整的可能。
第四步:确认抖动为来回变化
持续监听 innerWidth,触发一次 DOM 切换后,观察到变化序列为:
视口宽度短暂变化后弹回原值,确认是抖动行为。
QQ 浏览器左侧边栏在开启状态下,页面 DOM 发生变化时,侧边栏会重新进行布局计算,导致自身宽度出现短暂的 1px 或 10px 变化,从而挤压页面视口宽度,触发真实的 resize 事件。抖动结束后视口宽度弹回原值。
这是 QQ 浏览器自身 UI 层的 bug,与页面代码无关,无法从网页侧阻止事件触发。
利用"抖动会弹回原值、且幅度极小"的特性,用防抖 + 阈值两层过滤屏蔽噪声:
两层过滤的分工:
| 过滤层 | 作用 |
|---|---|
| 防抖 150ms | 等待抖动结束,过滤来回弹动(最终值未变)的噪声 |
| 阈值 5px | 过滤单方向小幅噪声(最终值虽变但幅度极小) |
各场景表现:
| 场景 | innerWidth 变化 | 最终变化幅度 | 结果 |
|---|---|---|---|
| 侧边栏 1px 抖动 | 1964 → 1965 → 1964 | 0px | 忽略 ✅ |
| 侧边栏 10px 抖动 | 1964 → 1974 → 1964 | 0px | 忽略 ✅ |
| 用户拖拽窗口 | 1964 → 1200 | 764px | 响应 ✅ |
遇到 resize 事件异常触发时,首先通过同时观测 innerWidth 和 clientWidth 来判断问题根源:
clientWidth 变化 → 页面内部问题(滚动条、布局)innerWidth 也变化 → 浏览器 UI 层问题,页面侧只能做防御性过滤对于无法修复的浏览器兼容性问题,防抖 + 阈值是通用的防御手段。