节流能强制滚动事件在指定间隔内最多执行一次,通过时间戳比较控制频率,比防抖更适滚动场景;手写函数仅需几行,可与passive配合提升性能,必要时结合requestAnimationFrame或监听特定容器优化。

滚动事件(scroll)非常频繁,尤其在快速滚动时每秒可能触发数十次,直接绑定处理函数会导致严重性能问题。节流(throttle)能强制函数在指定时间间隔内最多执行一次,是控制滚动事件频率的常用手段。
节流的核心逻辑:固定间隔内只执行首次调用
节流不是“等停了再执行”,而是在设定的时间窗口(如 100ms)内,仅让第一次触发的回调运行,后续调用被忽略,直到窗口重置。这比防抖(debounce)更适合滚动场景——防抖会延迟到停止后才执行,而滚动中常需及时响应(如吸顶导航、懒加载临界判断)。
- 关键点:用时间戳或定时器记录上一次执行时间
- 每次触发时比较当前时间与上次执行时间之差
- 差值大于阈值才执行,并更新“上次执行时间”
手写一个轻量节流函数(推荐)
不依赖 Lodash,几行代码即可实现可靠节流:
function throttle(fn, delay) {
let lastTime = 0;
return function (...args) {
const now = Date.now();
if (now - lastTime >= delay) {
fn.apply(this, args);
lastTime = now;
}
};
}使用方式:
立即学习“Java免费学习笔记(深入)”;
const handleScroll = throttle(() => {
console.log('滚动中,但每 100ms 最多执行一次');
}, 100);
<p>window.addEventListener('scroll', handleScroll);注意 passive 和节流的配合
现代浏览器推荐为滚动事件添加 { passive: true } 提升流畅度(避免阻塞主线程),但它与节流不冲突,可同时使用:
window.addEventListener('scroll', handleScroll, { passive: true });⚠️ 注意:如果节流函数内部需要调用 event.preventDefault()(比如阻止默认滚动),则不能设 passive: true,否则会报错。多数纯监听场景(如计算位置、触发懒加载)无需阻止默认行为,放心启用 passive。
实际应用中的常见优化点
-
结合 requestAnimationFrame 更顺滑:若节流后仍觉卡顿(比如做视差或实时定位),可用
requestAnimationFrame替代时间戳节流,确保在下一帧执行,与屏幕刷新同步 -
只监听必要区域:避免全局
window.scroll,优先监听特定容器(如document.querySelector('.list').addEventListener('scroll', ...)) -
滚动结束补一次(可选):某些场景(如滚动到底部加载更多)需确保最后一次滚动也被捕获,可在节流外加一个
scrollend事件监听(Chrome 112+ 支持,或用防抖兜底)


















