节流函数旨在防止滚动事件高频触发导致重复加载、卡顿或请求堆积;其核心是控制“判断是否该加载”的频率,而非降低滚动速度。

节流函数不是为了“让滚动变慢”,而是防止滚动事件高频触发导致重复加载、卡顿或请求堆积。它和无限滚动配合的关键,是把“判断是否该加载”这个动作控制在合理频率内,而不是每次滚动都检查一次。
为什么必须加节流
滚动事件每秒可能触发几十次,尤其在惯性滚动或触屏滑动时。如果不加限制:
- 同一位置反复触发 loadMore(),造成重复请求
- 频繁计算 scrollHeight / scrollTop 等 DOM 属性,拖慢主线程
- 短时间内插入大量 DOM 节点,引发重排重绘抖动
- 组件卸载后监听器未清理,引发内存泄漏
用定时器方式实现节流(推荐)
这是最稳定、易理解、无第三方依赖的写法。核心是“有任务在排队就不新增”:
let isLoading = false;
let throttleTimer = null;
<p>function throttleScrollHandler() {
if (throttleTimer) return;</p><p>throttleTimer = setTimeout(() => {
const { scrollHeight, clientHeight } = document.documentElement;
const scrollTop = window.scrollY || window.pageYOffset;
const threshold = 150; // 提前 150px 触发</p><pre class='brush:php;toolbar:false;'>if (scrollTop + clientHeight >= scrollHeight - threshold && !isLoading) {
isLoading = true;
loadMore().finally(() => {
isLoading = false;
throttleTimer = null;
});
} else {
throttleTimer = null;
}}, 160); // 约每秒 6 次,兼顾响应与性能 }
window.addEventListener('scroll', throttleScrollHandler);
立即学习“Java免费学习笔记(深入)”;
注意:这里用了 isLoading 锁 + throttleTimer 双保险,既防重复触发,也防加载中再次进入节流队列。
用 IntersectionObserver 替代 scroll 监听(现代优选)
它不依赖滚动事件,天然无频发问题,且自动处理 iframe、滚动方向、懒加载等边界场景:
const sentinel = document.createElement('div');
sentinel.className = 'infinite-sentinel';
document.body.appendChild(sentinel);
<p>const observer = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting && !isLoading) {
isLoading = true;
loadMore().finally(() => isLoading = false);
}
},
{ threshold: 0.05 } // 元素 5% 进入视口即触发
);</p><p>observer.observe(sentinel);对应 CSS 只需保证 sentinel 在页面末尾:
.infinite-sentinel { height: 1px; margin-top: -1px; }关键细节不能漏
- 永远用 document.documentElement 获取 scrollHeight / clientHeight / scrollTop,不用 body —— Safari 和部分安卓浏览器里 body.scrollTop 恒为 0
- 阈值(threshold)要设合理:太小(如 0)用户已到底部才触发,体验卡顿;太大(如 500)易误触发,尤其在内容动态变化时
- 加载完成后再移除节流锁,建议用 finally() 或 Promise.finally,避免异常导致锁永久挂起
- 页面销毁时记得清理:removeEventListener 或 unobserve,尤其 SPA 场景下
不复杂但容易忽略


















