节流的核心是将“判断是否触底”从高频滚动中抽离,只在滚动停止后且有新内容可加载时执行,并缓存容器尺寸以减少重排;推荐结合IntersectionObserver监听占位符实现更稳定加载。

节流在多列瀑布流滚动加载图片时,核心是把“判断是否触底”这个计算从高频滚动中抽离出来,只在合适时机执行,避免重复、无效的 DOM 高度测量和位置比对。
为什么滚动中直接判断会出问题
瀑布流布局每列高度不一,页面总高度 = 最高列高度;而“是否触底”的判断依赖 scrollTop + clientHeight >= scrollHeight。但:
- 滚动事件每秒触发几十次,每次都重新读取
scrollHeight和clientHeight(强制重排) - 图片异步加载导致列高动态变化,
scrollHeight在加载中持续波动 - 用户快速滚动时,可能连续触发多次加载请求,造成重复请求或错失加载时机
节流的关键:绑定时机 ≠ 触发时机
不能简单对 scroll 事件加个 200ms 节流就完事。真正要节流的是「触底检测逻辑」,且需配合两个关键策略:
- 只在滚动停止后检测:用节流确保至少 100–150ms 内无新滚动才执行判断(防抖+节流混合更稳)
-
仅当有新内容可加载时才检测:维护一个
canLoadMore = true状态,加载开始设为false,成功/失败后恢复;避免滚动时反复触发空检测 -
缓存滚动容器的尺寸信息:把
clientHeight、当前scrollTop提前读出,传入节流函数,避免内部再读 DOM(减少重排)
推荐实现方式(轻量、无依赖)
下面是一个生产可用的节流检测片段:
立即学习“Java免费学习笔记(深入)”;
let canLoadMore = true;
let pendingCheck = false;
<p>function checkIfNearBottom() {
if (!canLoadMore || pendingCheck) return;</p><p>const container = document.documentElement || document.body;
const scrollTop = container.scrollTop || window.pageYOffset;
const clientHeight = container.clientHeight;
const scrollHeight = container.scrollHeight;</p><p>// 预留 200px 提前触发(防滚动过快错过)
if (scrollTop + clientHeight >= scrollHeight - 200) {
pendingCheck = true;
loadNextBatch().finally(() => {
pendingCheck = false;
// 可选:加载后立即再检查一次(应对图片撑开高度但未触发新滚动的情况)
setTimeout(() => {
if (canLoadMore && !pendingCheck) checkIfNearBottom();
}, 10);
});
}
}</p><p>// 滚动节流:100ms 内只响应最后一次
let scrollTimer;
window.addEventListener('scroll', () => {
clearTimeout(scrollTimer);
scrollTimer = setTimeout(checkIfNearBottom, 100);
});</p>进阶建议:结合 IntersectionObserver 更可靠
纯滚动节流在移动端或 Safari 下仍有兼容和精度问题。更健壮的做法是:
- 在瀑布流末尾插入一个
<div id="loader-trigger"></div> - 用
IntersectionObserver监听它是否进入视口,触发加载 - 加载完成后,将该占位符移到新一批数据末尾(或复用)
- 此时无需手动读取
scrollHeight,也规避了滚动抖动、缩放、iframe 等干扰
这种方式本质是把“计算时机”交给浏览器原生的可见性判断,比基于滚动事件的节流更精准、更轻量、更稳定。


















