为什么不监听 scroll
scroll 事件每帧都可能触发,处理函数稍重一点就会掉帧。更重要的是,滚动事件本身不告诉你"用户看到了什么",你只能自己算元素位置。
哨兵模式
在列表底部放一个空元素当哨兵,然后观察它:
const observer = new IntersectionObserver((entries) => {
if (entries[0].isIntersecting) loadMore();
});
observer.observe(sentinel);
哨兵进入视口,就说明用户滚到底了。
三个必须处理的细节
- 重复触发:加载中要直接返回,否则一次滚动会发好几个请求。
- 没有更多:
hasMore为 false 时断开 observer,并把哨兵从 DOM 里移除。 - 卸载清理:组件卸载时必须
disconnect(),否则回调会打到已经不存在的组件上。
结束提示
全部加载完之后要明确告诉用户"已经到底了"。没有结束提示的无限滚动,用户会一直怀疑是不是卡住了。
什么时候不要用
文章超过五十篇之后,无限滚动会让用户彻底失去位置感。到那时更合适的做法是分页或者"加载更多"按钮。