节流是防止滚动事件频繁触发导致性能问题的关键手段,通过限制检测频率(如每150ms最多执行一次),避免重复计算、多次请求和主线程卡顿。

节流是防止滚动事件频繁触发导致性能问题的关键手段。无限滚动中,如果每次 scroll 都立即检查是否触底,可能一秒触发几十次,造成重复判断、资源浪费甚至卡顿。用节流把检测频率控制在合理范围(如每 100–150ms 最多执行一次),既保证响应及时,又避免过度消耗。
为什么必须加节流?
滚动事件非常密集——用户轻轻一滑,浏览器可能在几十毫秒内派发十几次 scroll。不加节流时:
- 反复计算
scrollTop、scrollHeight和clientHeight - 频繁调用
loadMore()(即使还没到底) - 容易触发多次请求,尤其在网速慢或接口延迟时更明显
- 影响主线程,造成滚动卡顿或页面抖动
节流的两种常用实现方式
推荐使用 setTimeout + 标志位的方式,轻量、易懂、无依赖:
let isThrottled = false;
function handleScroll() {
if (isThrottled) return;
isThrottled = true;
<p>setTimeout(() => {
const { scrollHeight, clientHeight } = document.documentElement;
const scrollTop = window.scrollY || window.pageYOffset;
const threshold = 200; // 距底部200px内触发</p><pre class="brush:php;toolbar:false;">if (scrollTop + clientHeight >= scrollHeight - threshold) {
loadMore();
}
isThrottled = false;
}, 150); }
window.addEventListener('scroll', handleScroll);
另一种是封装成可复用的节流函数:
function throttle(fn, delay) {
let timer = null;
return function (...args) {
if (!timer) {
timer = setTimeout(() => {
fn.apply(this, args);
timer = null;
}, delay);
}
};
}
<p>const throttledCheck = throttle(() => {
// 检测触底逻辑
}, 150);</p><p>window.addEventListener('scroll', throttledCheck);</p>
节流阈值怎么选?
150ms 是较稳妥的平衡点:
- 小于 100ms:节流效果弱,仍可能高频触发
- 大于 200ms:用户快速滚动时可能“错过”触底时机,出现加载延迟
- 移动端可略放宽至 180ms(触摸滚动惯性更强)
- 若配合
getBoundingClientRect()判断触底,建议保持 150ms 左右,因其计算开销略高
节流要和防重载配合使用
节流只管“少执行”,不管“别重复”。还需用加载状态锁确保同一时间只发一个请求:
let isLoading = false;
async function loadMore() {
if (isLoading) return;
isLoading = true;
<p>try {
const data = await fetch('/api/items?offset=...');
render(data);
} finally {
isLoading = false;
}
}</p>
节流 + 加载锁双保险,才能真正避免重复请求、闪烁、抖动等问题。











