前言
滚动加载是前端经常遇到的业务场景,主要是实现列表的加载,其实质跟分页没多大区别,如果不是特别要求,可以使用分页替代,综合性能和用途做以下几种方案。
方案
1、原生js实现
1、通过onScrollEvent监听被div包裹的元素是否滚动到底部了,再次发起请求,ps:
ref是react的语法,其目的也就是存储一个变量,原生可以用let定义一个变量存储即可
<div ref={scrollElement} onScrollCapture={onScrollEvent}>
<Table />
</div>
const onScrollEvent = (e) => {
/** 解决横向滚动误触发判断 */
if (e.target.scrollTop === scrollElement.current) return false;
scrollElement.current = e.target.scrollTop;
if (e.target.scrollTop === e.target.scrollHeight - e.target.clientHeight) {
// setTableLoading(true);
// console.log('我到底了,重新请求数据');
setTimeout(() => {
// setTableLoading(false);
}, 100);
}
};
2、通过IntersectionObserver实现
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>滚动加载示例</title>
<style>
body {
margin: 0;
padding: 0;
font-family: Arial, sans-serif;
}
.container {
display: flex;
flex-wrap: wrap;
margin: 20px;
}
.item {
flex-basis: 200px;
height: 200px;
margin: 10px;
text-align: center;
background-color: #f2f2f2;
font-size: 36px;
line-height: 200px;
}
.loading {
height: 50px;
text-align: center;
font-size: 24px;
color: #999;
margin-bottom: 20px;
}
::-webkit-scrollbar {
width: 6px;
}
::-webkit-scrollbar-thumb {
border-radius: 3px;
-webkit-box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2);
background-color: rgba(0, 0, 0, 0.2);
}
::-webkit-scrollbar-track {
-webkit-box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2);
border-radius: 3px;
background-color: #f2f2f2;
}
</style>
</head>
<body>
<div class="container"></div>
<div class="loading">加载中...</div>
<script>
// 初始化数据
const data = [];
for (let i = 1; i <= 20; i++) {
data.push(i);
}
// 创建 IntersectionObserver 实例
const observer = new IntersectionObserver(
(entries) => {
// 遍历所有进入视口的元素
entries.forEach((entry) => {
// 如果元素是 loading 元素,且进入了视口,则执行加载更多数据的回调函数
if (
entry.target === document.querySelector(".loading") &&
entry.isIntersecting
) {
loadMore();
}
});
},
{
root: null, // 使用浏览器视口作为根元素
rootMargin: "0px", // 不设置边距
threshold: 1.0, // 只有当目标元素全部进入视口时触发回调函数
}
);
// 页面加载完毕后,先加载前面的数据
window.onload = () => {
const container = document.querySelector(".container");
data.forEach((item) => {
const el = document.createElement("div");
el.className = "item";
el.textContent = item;
container.appendChild(el);
});
// 开始监听 loading 元素
observer.observe(document.querySelector(".loading"));
};
// 加载更多数据的回调函数
function loadMore() {
// 模拟异步请求数据
const newData = [];
for (let i = 1; i <= 10; i++) {
newData.push(data.length + i);
}
// 将新数据添加到页面中
const container = document.querySelector(".container");
newData.forEach((item) => {
const el = document.createElement("div");
el.className = "item";
el.textContent = item;
container.appendChild(el);
});
// 如果数据已经加载完毕,则取消监听 loading 元素
if (data.length >= 50) {
observer.unobserve(document.querySelector(".loading"));
document.querySelector(".loading").textContent = "已加载全部数据";
} else {
data.push(...newData);
}
}
</script>
</body>
</html>
2、使用第三方组件
可以使用infinite-scroll
第三方库实现,vue、react都有相对应的版本
react:react-infinite-scroll-component
vue:vue-infinite-scroll
3、虚拟列表
如果页面使用原生js等方案时,虽然可以达到目的,但是随着页面滚动,页面的dom元素会大量增加,综合性能考虑的话,推荐使用虚拟表格,最新版本的antd和element-plus都已支持虚拟表格方案,自行参考对应写法
react:https://ant.design/components/table-cn#components-table-demo-virtual-list
vue:https://element-plus.org/zh-CN/component/table-v2.html