背景
- H5项目中需要实现一个上滑加载更多列表数据的需求。
- 选择方案:(1)antd-mobile:兼容性好,实现简单,需要引用第三方库;(2)自己实现:可能有些细节我还不太了解,需要花时间,不需要引用库。
- 结论:考虑到稳定性就用antd-mobile;另外发现自己实现的话有些内容其实不太理解,是可以借此机会顺便学习下的。本文就对于自己实现的方案做一下记录。
方案
1.监听scrll事件+算滚动到最底部距离+防抖+节流
1.1 防抖
(1)含义: 多次连续触发执行某一事件改为最后一次触发后的一段时间后执行一次事件;
(2)场景:输入框输入后查询提示词;
(3)实现:
function debounce(func, delay) {
let timer = null;
return function () {
timer && clearTimeout(timer);
timer = setTimeout(() => {
func.apply(this, arguments);
}, delay);
};
}
scrollBox.addEventListener("scroll", debounce(onScroll, 500));
//或者
function debounce(func, delay) {
return function () {
func.timer && clearTimeout(func.timer);
func.timer = setTimeout(() => {
func.apply(this, arguments);
}, delay);
};
}
//使用:
scrollBox.addEventListener("scroll", debounce(onScroll, 500));
//如果要获取事件event
scrollBox.addEventListener("scroll", (e) => debounce(onScroll, 500));
注:addEventListener在定义的时候,第2个参数会被立即执行。(所以通常传函数,而不是函数调用)
1.2 节流
(1)含义: 多次触发时每隔指定时间执行一次;
(2)场景:scroll和resize这类高频事件的处理函数如果比较消耗性能可以用节流;防重复点击
(3)实现:
function throttle(fn, delay) {
let canrun = true;
return function () {
if (!canrun) return;
canrun = false;
setTimeout(() => {
fn.apply(this, arguments);
canrun = true;
}, delay);
};
}
//使用:
scrollBox.addEventListener("scroll", throttle(onScroll, 400));
1.3 实现:使用节流限制scroll的事件处理频率,使用防抖处理用户请求,throshold:滑动到距离底部最多为throshold的位置时请求接口:
function onScroll() {
if (scrollBox.scrollHeight -scrollBox.scrollTop -document.documentElement.clientHeight
<= throshold
) {
//防抖:处理请求更多列表
sendAjax.timer && clearTimeout(sendAjax.timer);
sendAjax.timer = setTimeout(() => {
sendAjax();
}, 3000);
}
}
function throttle(fn, delay) {
let canrun = true;
return function () {
if (!canrun) return;
canrun = false;
setTimeout(() => {
fn.apply(this, arguments);
canrun = true;
}, delay);
};
}
//节流:监听滚动事件
scrollBox.addEventListener("scroll", throttle(onScroll, 400));
注意:
1.节流的时间间隔需要小于防抖的时间间隔;否则每一个节流的delay期间,都会请求接口。 2.这个方案有一个缺陷:若在threshold距离内滚动过程中有停止滚动然后再继续滚动的情况,且threshold足够大,可以在滚动过程中多次触发,那么依然会触发多次的接口请求,需要对请求的页码防重处理。
2.使用IntersectionObserver
需要再列表底部预留一个占位块,监听这个块,若进入可视范围,就请求接口,并重新调整占位块的位置。
window.onload = function () {
const sentry = document.querySelector("#sentry");
if (sentry) {
window.observer = new IntersectionObserver(function (entries) {
//采用交叉比例的方式不准,快速滑动时可能会捕捉不到交叉比例>0的时机
// if (entries[0].intersectionRatio <= 0) return;
//测试用这个是可以的
if (entries[0].isIntersecting) {
sendAjax();
}
});
observer.observe(sentry);
}
};
注意:
1.快速滑动的时候,有时entries[0].intersectionRatio > 0 会捕获不到,可以用entries[0].isIntersecting作为判断条件。
2.监听需要再onload之后。
参考文章:
使用Intersection Observer API实现图片懒加载与无限滚动:[juejin.cn/post/725449…]
防抖和节流[juejin.cn/post/684490…]
移动端「上滑-加载更多」原理浅[cloud.tencent.com/developer/a…]
前端性能优化篇:防抖与节流: