思路
- 监听列表元素的滚动事件,滚动到底部的时候,加载下一页的数据
- 监听数据加载,判断是否已全部加载结束
实现
监听滚动事件
- 为列表元素 listBox 绑定 scroll 事件进行监听
<div class="listBox" @scroll="watchScroll">
<div class="sinList" v-for="(item, index) in dataList" :key="index">
{{ item.label }}--{{ item.value }}
div>
<div class="dataDesc">
{{
dataList.length == 0
? "暂无数据"
: loading && !finished
? "加载中"
: "没有更多了"
}}
div>
div>
- scrollTop:listBox 滚动时,向上卷曲出去的距离
- clientHeight:listBox 滚动时,可视的高度
- scrollHeight:listBox 的总高度
- 滚动的过程中,scrollTop 不断变大,直至 scrollTop + clientHeight == scrollHeight 时,表示 listBox 已经滚动到了底部
- 滚动到底部时,如果 finished 为 false,表示数据还未加载完,此时需要调用接口,加载下一页的数据
- 加载完数据后,scrollHeight 变大
- 继续滚动,scrollTop 变大,直至再次 scrollTop + clientHeight == scrollHeight …
- 为了优化体验,可以使“调用接口”提前触发,即在距离底部一定距离时,就触发接口加载。
- 此距离取个名字为安全距离 safeHeight,即 scrollTop + clientHeight >= scrollHeight - safeHeight 时,触发接口调用
![PC端实现滚动分页懒加载_第1张图片](http://img.e-com-net.com/image/info8/ef928ba6d91f44ffaeb0d31f086bc06e.jpg)
watchScroll(e) {
let scrollTop = e.target.scrollTop;
let clientHeight = e.target.clientHeight;
let scrollHeight = e.target.scrollHeight;
let saveHeight = 30;
let tempVal = scrollTop + clientHeight + saveHeight;
console.log(
"scrollTop:" +
scrollTop +
";clientHeight:" +
clientHeight +
";scrollHeight:" +
scrollHeight,
";tempVal:" + tempVal
);
if (tempVal >= scrollHeight) {
console.log("滚动到底了");
if (!this.finished && !this.switch) {
this.getData();
}
this.switch = true;
} else {
console.log("还没有滚动到底");
}
},
请求接口数据
- 当 res.data 的条数 < pageSize 的时候,说明数据已经请求完毕
- 当 dataList 的条数 == total 时,说明数据已经请求完毕
- 以上两种情况可以将全局变量 finished 置为 true
- 其他情况视为加载还未结束,将 finished 置为 false,页码自加 1,改变滚动锁的状态
- 滚动锁 switch 用来防止接口被重复请求。当滚动到达底部时,请求接口时,将 switch 置为 true,即使再次触发 tempVal >= scrollHeight 也不再执行数据请求。
- 直到请求到数据,并且判断数据还未加载完,则将 switch 置为 false,可以进入下次滚动判断
getData() {
let params = {
pageSize: this.pageSize,
pageNum: this.pageNum,
};
this.loading = true;
this.mockList(params).then((res) => {
this.loading = false;
if (res.code == 200) {
if (res.data && res.data.length > 0) {
this.dataList = this.dataList.concat(res.data);
this.total = res.totalCount;
if (
res.data.length < this.pageSize ||
this.dataList.length >= this.total
) {
this.finished = true;
} else {
this.finished = false;
this.pageNum += 1;
this.switch = false;
}
}
}
});
},
图示
![PC端实现滚动分页懒加载_第2张图片](http://img.e-com-net.com/image/info8/d41cc18ef60a44f4978619c2aed0880e.jpg)
![PC端实现滚动分页懒加载_第3张图片](http://img.e-com-net.com/image/info8/c8e18c3081664629a30c24c9d0651534.jpg)
完整代码
<template>
<div class="page">
<div class="listBox" @scroll="watchScroll">
<div class="sinList" v-for="(item, index) in dataList" :key="index">
{{ item.label }}--{{ item.value }}
div>
<div class="dataDesc">
{{
dataList.length == 0
? "暂无数据"
: loading && !finished
? "加载中"
: "没有更多了"
}}
div>
div>
div>
template>
<script>
export default {
data() {
return {
pageSize: 10,
pageNum: 1,
total: 0,
loading: false,
finished: true,
switch: false,
dataList: [],
};
},
mounted() {
this.getData();
},
methods: {
getData() {
let params = {
pageSize: this.pageSize,
pageNum: this.pageNum,
};
this.loading = true;
this.mockList(params).then((res) => {
this.loading = false;
if (res.code == 200) {
if (res.data && res.data.length > 0) {
this.dataList = this.dataList.concat(res.data);
this.total = res.totalCount;
if (
res.data.length < this.pageSize ||
this.dataList.length >= this.total
) {
this.finished = true;
} else {
this.finished = false;
this.pageNum += 1;
this.switch = false;
}
}
}
});
},
mockList(params) {
console.log("请求接口 params", params);
return new Promise((resolve, reject) => {
let data = [];
for (let i = 0; i < 58; i++) {
data.push({
value: "value" + i,
label: "label" + i,
});
}
let tempArr = data.splice(
params.pageSize * (params.pageNum - 1),
params.pageSize * params.pageNum
);
let res = {
data: tempArr,
totalCount: data.length,
pageSize: params.pageSize,
pageNum: params.pageNum,
code: 200,
msg: "success",
};
setTimeout(() => {
resolve(res);
}, 100);
});
},
watchScroll(e) {
let scrollTop = e.target.scrollTop;
let clientHeight = e.target.clientHeight;
let scrollHeight = e.target.scrollHeight;
let saveHeight = 30;
let tempVal = scrollTop + clientHeight + saveHeight;
console.log(
"scrollTop:" +
scrollTop +
";clientHeight:" +
clientHeight +
";scrollHeight:" +
scrollHeight,
";tempVal:" + tempVal
);
if (tempVal >= scrollHeight) {
console.log("滚动到底了");
if (!this.finished && !this.switch) {
this.getData();
}
this.switch = true;
} else {
console.log("还没有滚动到底");
}
},
},
};
script>
<style lang="scss" scoped>
.page {
width: 100%;
height: 100vh;
background: #f1f1f1;
.listBox {
background: #fff;
width: 600px;
height: 300px;
overflow: auto;
padding: 20px;
box-sizing: border-box;
.sinList {
height: 40px;
line-height: 40px;
}
.dataDesc {
color: #999;
text-align: center;
}
}
}
style>