节流是解决滚动事件频繁触发导致性能卡顿的有效方案,核心是在固定时间间隔内最多执行一次函数,常用时间戳或requestAnimationFrame实现,兼顾性能与响应需求。

页面滚动事件触发太频繁,直接监听 scroll 容易造成性能卡顿。节流的核心是“固定时间间隔内最多执行一次”,对滚动这种高频事件特别有效。
滚动节流的关键逻辑是:控制函数不被每像素滚动都调用,而是按设定的时间窗口(比如 16ms、100ms 或 300ms)稳定执行一次。
为什么滚动需要节流
浏览器在用户快速滚动时,每秒可能触发几十甚至上百次 scroll 事件。如果每次都在回调里做 DOM 查询、计算位置或发起请求,会严重挤占主线程,导致掉帧、卡顿,甚至阻塞其他交互。
例如检测是否滚动到底部:
- 没节流:滚动 1 秒 → 执行 80 次判断逻辑
- 加节流(300ms):滚动 1 秒 → 最多执行 3~4 次判断
手写一个可靠节流函数(带立即执行支持)
以下是一个兼顾实用性与健壮性的节流实现,支持首次立即执行(可选),并正确传递 this 和参数:
function throttle(fn, delay, options = {}) {
const { leading = true, trailing = true } = options;
let lastTime = 0;
let timer = null;
<p>return function(...args) {
const now = Date.now();</p><pre class="brush:php;toolbar:false;">if (leading && now - lastTime >= delay) {
fn.apply(this, args);
lastTime = now;
} else if (!timer && trailing) {
timer = setTimeout(() => {
fn.apply(this, args);
lastTime = Date.now();
timer = null;
}, delay - (now - lastTime));
}
}; }
使用方式:
const handleScroll = () => {
const scrollTop = window.scrollY || document.documentElement.scrollTop;
const { scrollHeight, clientHeight } = document.documentElement;
<p>if (scrollTop + clientHeight >= scrollHeight - 50) {
console.log('快到底部了,准备加载更多');
}
};</p><p>window.addEventListener('scroll', throttle(handleScroll, 200));</p><div class="aritcle_card flexRow artxards">
<div class="artcardd flexRow">
<a class="aritcle_card_img" rel="nofollow" href="/xiazai/skill10340" title="arcgis-js"><img
src="https://img.php.cn/upload/skill/001/246/273/6adaf20e352aa140.jpg" alt="arcgis-js" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
<div class="aritcle_card_info flexColumn">
<a rel="nofollow" href="/xiazai/skill10340" title="arcgis-js" class="overflowclass">arcgis-js</a>
<p class="overflowclass">提供 ArcGIS Maps SDK for JavaScript 开发辅助,涵盖 2D/3D 地图视图、图层管理、数据可视化、空间分析等功能模块的参考和示例指导。</p>
</div>
<a rel="nofollow" href="/xiazai/skill10340" title="arcgis-js" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span>
</a>
</div>
</div>
更轻量的推荐写法(适合多数场景)
如果不需要复杂配置,用时间戳判断是最简洁、无副作用的方式:
function throttle(fn, delay) {
let previous = 0;
return function(...args) {
const now = Date.now();
if (now - previous >= delay) {
fn.apply(this, args);
previous = now;
}
};
}
<p>window.addEventListener('scroll', throttle(() => {
console.log('滚动位置:', window.scrollY);
}, 100));</p>
✅ 优点:无定时器、无内存泄漏风险、上下文和参数天然保留。
⚠️ 注意:它不会补最后一次滚动(即停止滚动后不额外触发),但对“滚动中持续检查”类需求完全够用。
搭配 requestAnimationFrame 更顺滑(进阶选择)
若目标是动画类响应(如视差、吸顶导航),requestAnimationFrame 比定时器更精准匹配屏幕刷新节奏(约每 16ms 一次):
let ticking = false;
function rafThrottle(fn) {
return function(...args) {
if (!ticking) {
requestAnimationFrame(() => {
fn.apply(this, args);
ticking = false;
});
ticking = true;
}
};
}
<p>window.addEventListener('scroll', rafThrottle(() => {
// 这里放样式更新、位置计算等视觉相关逻辑
header.classList.toggle('sticky', window.scrollY > 100);
}));</p>
? 适用:需频繁读写 DOM 样式且追求 60fps 的场景;不适合用于网络请求或数据加载。
节流不是万能开关,关键是选对时机——滚动中查位置用时间戳节流,做视觉反馈优先用 rAF,而真正要“确保最后一次也响应”才考虑带定时器的完整版。实际项目中,200ms 左右的延迟对用户几乎无感,却能让性能提升明显。










