节流函数是窗口滚动事件中必须落地的性能保障措施,因其可控制高频触发、避免重排堆积和主线程阻塞,并需结合passive、requestAnimationFrame及IntersectionObserver进一步优化。

节流函数在窗口滚动事件中不是“可选优化”,而是必须落地的性能保障措施。滚动事件天然高频,不加控制极易引发卡顿、重排堆积和主线程阻塞。
为什么必须用节流,而不是直接监听
用户快速滚动时,scroll 事件每秒可能触发 60–150 次。若每次都在回调里读取 getBoundingClientRect()、修改 DOM 或调用 offsetTop,浏览器会反复触发重排(reflow)和重绘(repaint),导致帧率骤降、滚动粘滞。节流通过强制设定最小执行间隔(如 100ms 或 16ms),把不可控的高频调用收束为稳定节奏,让逻辑真正可预测、可维护。
推荐使用定时器 + 时间戳混合版节流
纯时间戳版首次立即执行但可能漏掉最后一次滚动;纯定时器版能兜底,但初始响应稍慢。生产环境建议用兼顾二者优点的实现:
- 记录上一次执行时间,保证基础周期性
- 同时维护一个定时器,在剩余时间不足时“补发”一次,确保末次滚动不被丢弃
- 始终用
fn.apply(this, arguments)保留原始上下文和参数
配合 passive 和 requestAnimationFrame 效果更佳
节流只是第一步。进一步提升流畅度需组合使用:
立即学习“Java免费学习笔记(深入)”;
- 添加事件监听时启用
{ passive: true },让浏览器跳过 preventDefault 判断,提前合成滚动帧 - 将实际 DOM 更新逻辑包裹进
requestAnimationFrame,使其与屏幕刷新同步,避免强制同步布局(layout thrashing) - 避免在节流回调中反复读写布局属性;位置信息尽量缓存,仅在 resize 后更新一次
什么场景该换 IntersectionObserver
如果你的滚动逻辑本质是“某元素是否进入视口”,比如图片懒加载、动画触发动画条、吸顶导航判断——直接放弃 scroll + 节流 + getBoundingClientRect 的组合。改用 IntersectionObserver:
- 由浏览器原生调度,零 JS 主线程开销
- 支持阈值配置、根容器指定、惰性初始化
- 自动处理滚动、缩放、iframe 嵌套等复杂边界


















