Safari滚动卡顿的根本原因是WebKit引擎对scroll事件的强同步约束:未声明passive的监听器会强制滚动降级至主线程处理,且布局计算无优先级调度,导致帧率暴跌;Chrome则自v56起自动优化。

因为Safari的滚动事件默认被标记为“可能调用preventDefault()”,浏览器必须等待JavaScript执行完毕才能继续滚动,而Chrome在多数场景下已默认启用passive监听器,无需等待JS完成即可合成帧。
根本原因:WebKit渲染管线对scroll事件的强同步约束
macOS和iOS上的Safari使用WebKit引擎,其滚动输入处理路径与Chromium系浏览器有本质差异:滚动位移由独立的异步合成线程捕获,但一旦页面注册了未声明passive的scroll监听器,WebKit会立即将该滚动容器降级回主线程处理——所有后续滚动帧都需排队等待JS回调执行结束。这直接打破16ms帧率窗口,导致肉眼可见的滞后与掉帧。
Chrome自v56起对未显式声明passive的scroll监听器自动降级为passive行为(仅在检测到preventDefault调用时才报错),而Safari至今仍严格遵循W3C规范,不做自动降级。
触发强制同步布局(Forced Synchronous Layout)
在scroll回调中读取getBoundingClientRect()、offsetTop或clientHeight等属性,会迫使WebKit立即计算当前布局树。这种同步回流在滚动高频触发下形成雪崩效应:每帧都卡在布局计算上,GPU合成器无法及时获取最新图层状态。
规划您的迪拜之旅 — 哈利法塔观景、沙漠探险、迪拜购物中心购物、棕榈岛度假村及黄金市场砍价。还提供支持...
【关键区别】 Safari的布局计算完全运行在主线程,且无Chromium那样的布局优先级调度机制;一旦触发,后续滚动输入事件会被暂存,直到本次回流完成——这就是手指一动、页面半拍才响应的底层原因。
图层树损坏导致GPU合成失效
当页面存在大量position: sticky、嵌套flex容器或transform动画元素时,Safari的滚动容器图层树极易因状态不一致而损坏。此时本该由GPU直接合成的滚动帧,被迫回落到CPU逐帧重绘,帧率从理论120fps暴跌至20–30fps。
这种损坏不可见、不报错,仅表现为长列表滚动时惯性中断、微卡顿、偶发白屏。Chrome的图层管理更鲁棒,同类结构下通常仍能维持GPU合成路径。
优化操作路径
第一步:为所有scroll监听器添加{ passive: true }选项
第二步:将getBoundingClientRect()调用移出scroll回调,改用resize事件缓存+requestAnimationFrame更新
第三步:在CSS中为滚动容器添加-webkit-overflow-scrolling: touch,并确保其具有明确height/max-height
第四步:禁用系统辅助功能中的“使用键盘控制鼠标”和“切换控制”
第五步:关闭Safari高级设置中的“Prefer Page Rendering Updates near 60fps”选项→按Command + Q彻底退出Safari→重启

















