滚动事件需节流以避免性能问题,常用时间戳或requestAnimationFrame实现,前者稳定易懂,后者更精准顺滑且避免回流抖动。

滚动事件触发太频繁,直接绑定处理函数会导致性能问题,必须做频率控制。核心思路是限制单位时间内执行次数,常用节流(throttle)而非防抖(debounce),因为滚动中需要持续响应位置变化,而不是只响应停止后。
为什么 scroll 事件需要节流
用户快速滚动时,浏览器每秒可能触发数十次甚至上百次 scroll 事件。若每次都在回调中执行 DOM 操作、计算位置或发起请求,会造成主线程阻塞、页面卡顿、掉帧。节流能确保函数按固定间隔执行(如每 16ms 一次),既保持响应性,又避免过载。
手写简单节流函数(推荐用于轻量场景)
不依赖外部库时,可用时间戳方式实现可靠节流:
示例代码:
立即学习“Java免费学习笔记(深入)”;
function throttle(func, delay) {
let lastTime = 0;
return function (...args) {
const now = Date.now();
if (now - lastTime >= delay) {
func.apply(this, args);
lastTime = now;
}
};
}
<p>// 使用
window.addEventListener('scroll', throttle(() => {
console.log('滚动中,但每 16ms 最多执行一次');
}, 16));
- delay 设为 16ms 可逼近 60fps 渲染节奏,兼顾流畅与性能
- 该实现不使用定时器,无内存泄漏风险,适合长期监听
- 注意:this 和参数通过
apply正确传递,避免丢失上下文
用 requestAnimationFrame 实现更优节流
比时间戳更精准的方式是结合 requestAnimationFrame(rAF),让逻辑对齐浏览器重绘周期,避免强制同步布局(layout thrashing):
let isQueued = false;
<p>function rafThrottle(func) {
return function (...args) {
if (!isQueued) {
isQueued = true;
requestAnimationFrame(() => {
func.apply(this, args);
isQueued = false;
});
}
};
}</p><p>window.addEventListener('scroll', rafThrottle(() => {
const scrollTop = window.pageYOffset;
// 安全读取滚动位置并更新 UI
}));
- rAF 版本天然与屏幕刷新率同步,视觉更顺滑
- 适合需频繁读取
scrollTop、getBoundingClientRect()等会触发回流的操作 - 注意:rAF 回调中不要做耗时计算,否则仍会影响帧率
实际项目中的建议组合策略
真实业务往往需要平衡精度、兼容性和可维护性:
- 简单滚动监听(如吸顶导航)→ 用 16ms 时间戳节流,稳定易懂
- 涉及元素位置计算或视差效果 → 优先用 rAF 节流,避免布局抖动
- 需兼容 IE9+ 且逻辑复杂 → 可引入 lodash.throttle,配置 leading/trailing 行为
- 监听目标非 window(如自定义滚动容器)→ 确保事件绑定在正确元素上,并检查
overflow样式是否生效


















