窗口滚动节流核心是保底执行,推荐时间戳法(Date.now()比对),需正确传递this和event,必须设置passive: true以避免阻塞滚动,配置依用途选leading/trailing。

窗口滚动事件用节流控制,核心是限制回调函数在单位时间内最多执行一次,避免每像素滚动都触发计算,造成重排重绘或卡顿。重点不在“能不能少执行”,而在“必须保底执行”——哪怕用户快速拖到底部,中间也得有响应。
选对节流策略:时间戳法最稳
推荐用 Date.now() 比对上一次执行时间的方式,逻辑清晰、无定时器堆积风险:
- 每次滚动触发时,读取当前时间戳
- 与上次执行时间差值 ≥ 设定间隔(如 16ms),就立即执行并更新时间戳
- 否则跳过,不设定时器、不缓存参数
这种写法不会因连续高频触发而“饿死”,保证最小刷新节奏,适合 scroll 这类需持续反馈的场景。
注意 this 和 event 的正确传递
原生 scroll 回调里的 this 指向绑定元素(如 window),而节流函数返回的是新函数,默认丢失上下文:
- 必须用 fn.apply(this, arguments) 或 fn.call(this, ...arguments) 显式还原
- 如果需要 event 对象,建议在 addEventListener 中传参:
el.addEventListener('scroll', throttle(handleScroll, 16)),并在 handleScroll 里直接用形参接收 event - 避免在节流内部直接修改 event 属性,它可能被复用或已过期
别漏掉 passive: true 这个底层开关
现代浏览器对未声明 passive: true 的 scroll 监听器会强制同步执行回调,哪怕里面只有一行 console.log,也会阻塞滚动帧:
- 添加监听时务必带上:
window.addEventListener('scroll', throttledHandler, { passive: true }) - 但要注意:启用 passive 后,event.preventDefault() 不可用,所以如果业务需要阻止默认滚动行为(比如自定义滚动容器),就不能开 passive,此时更需靠节流压低频率
滚动节流的典型配置建议
根据用途微调选项:
- 做视差动画或实时位置判断 → 用 { leading: true, trailing: true },确保首尾都响应,不漏关键位置
- 仅更新状态、不依赖精确结束态(如记录滚动进度)→ 可设 trailing: false,省一次收尾调用
- 完全不想首次立即执行(极少见)→ 设 leading: false,第一次会等满间隔再触发
- 常见间隔值:16ms(≈60fps) 适合动画类;50–100ms 适合数据上报或懒加载判断


















