无限滚动必须用节流而非防抖,因防抖需用户停止滚动才触发,易导致下一页永不加载;节流则按固定间隔(如300ms)检查“滚动位置+视口高度≥总高度−阈值”是否满足,配合loading锁确保每500ms最多检查一次且不重复请求。

在无限滚动加载中,节流不是“防止重复请求”,而是“控制请求节奏”——确保滚动过程中不因高频触发而压垮接口或卡顿页面,同时保持数据加载的及时性。
为什么无限滚动必须用节流而不是防抖
防抖会等用户停止滚动才发请求,但用户可能一直滚、不停止,导致下一页数据永远不加载;节流则不管用户是否停下,只要滚动持续,就按固定间隔(如300ms)检查一次是否该加载,既防爆又保响应。
关键判断逻辑是:当前滚动位置 + 视口高度 ≥ 内容总高度 − 某个阈值(如200px),即“快到底了”,才触发加载。
节流函数要支持立即执行和状态重置
标准节流(时间戳版)第一次触发就执行,适合滚动检测;但要注意加载中状态需避免重复触发,所以建议加一个简单锁:
立即学习“Java免费学习笔记(深入)”;
- 用时间戳比对间隔,保证每500ms最多检查一次是否满足加载条件
- 加载开始时设 loading = true,请求返回或失败后设为 false
- 节流函数内部先判断 !loading,再决定是否执行加载逻辑
滚动监听 + 节流 + 加载守卫的完整写法
示例代码(无依赖、可直接运行):
function throttle(fn, delay) {
let last = 0;
return function(...args) {
const now = Date.now();
if (now - last >= delay) {
fn.apply(this, args);
last = now;
}
};
}
<p>let loading = false;
const loadMore = async () => {
if (loading) return;
loading = true;
try {
// 这里调用你的分页请求,比如 fetch(<code>/api/list?page=${nextPage}</code>)
console.log('发起第', nextPage, '页请求');
await new Promise(r => setTimeout(r, 800)); // 模拟请求
nextPage++;
} finally {
loading = false;
}
};</p><p>// 节流包装后的滚动处理器
const handleScroll = throttle(() => {
const { scrollTop, clientHeight } = document.documentElement;
const scrollHeight = document.body.scrollHeight;
if (scrollTop + clientHeight >= scrollHeight - 200) {
loadMore();
}
}, 300);</p><p>window.addEventListener('scroll', handleScroll);
实际部署还要注意几个细节
-
监听目标选 document.documentElement 还是 window? 推荐用
document.documentElement,兼容 better-scroll 等场景,且能准确读取 scrollTop -
阈值 200px 不是固定值,可根据卡片高度动态算,比如
scrollHeight - clientHeight - itemHeight * 2 -
加载完成要更新“是否还有下一页”状态,避免最后一页反复触发;可在响应中判断
data.length 后取消监听或置空节流函数


















