滚动事件处理应优先使用 requestanimationframe 封装的节流,配合 passive 和 will-change 优化;避免强制同步布局,dom 读写需分离,复杂逻辑移至 raf 或 web worker。

滚动事件本身不直接渲染,但它的处理函数常触发重排重绘——比如读取元素位置、修改 class、更新 DOM。若没做控制,一秒内几十次执行,主线程被反复抢占,帧率暴跌,页面就卡。防抖和节流不是“万能渲染开关”,而是帮你把高频回调压到可承受的节奏里,让浏览器有空喘气、按时渲染。
滚动场景优先用节流,不是防抖
用户滑动是连续动作,你往往需要“过程中响应”:比如滚动到某区域时懒加载图片、超过阈值时显示吸顶栏、实时计算视差位移。防抖要等滚动彻底停下才执行,中间关键时机全丢了;节流则保证每 16ms(≈60FPS)或自定义间隔内至少执行一次,既控频又保响应。
- 适合节流的典型操作:判断元素是否进入视口、切换导航栏样式、更新滚动进度条、驱动轻量级视差动画
- 防抖只在极少数终态判断时可用:例如“滚动到底部后触发下一页加载”,需确认用户真停了再发请求
用 requestAnimationFrame 封装节流最稳
比 setTimeout 或时间戳手写更优:它天然对齐屏幕刷新节奏,不会因定时器误差导致掉帧,也不用自己管清理和状态重置。
- 核心逻辑就两步:标记“已排队” → 用 rAF 调用函数 → 执行完清除标记
- 代码简洁可靠,无需手动 clearTimeout 或 Date.now() 比较
- 后台标签页自动暂停,省电且不干扰主流程
别忘了 passive 和 will-change 配合
光靠节流还不够。给 scroll 事件加 { passive: true },告诉浏览器“这个监听器不会调用 preventDefault”,浏览器就能跳过等待直接滚动,消除延迟感;对要做动画的元素加 will-change: transform,提前提示合成层提升,避免滚动中临时触发布局计算。
- passive 必须配合节流使用,否则仍可能阻塞滚动线程
- will-change 不要滥用,只加在真正需要频繁变换的元素上
处理函数内部也要轻量化
节流只是“少执行”,不是“随便写”。哪怕每秒只跑 5 次,如果每次都在查 DOM、算 layout、改样式,照样卡。
- DOM 读写分离:先批量读取 offsetTop/getBoundingClientRect,再统一写入 class 或 style
- 避免强制同步布局:不要在 scroll 回调里立刻读 clientWidth 后又改 width
- 复杂逻辑挪到 rAF 后或 Web Worker 中,主线程只做必要判断











