问题描述: 成千上万条数据的长列表,用户刷屏次数多,渲染大量dom节点,造成数据加载慢,画面卡顿,影响用户的交互体验,也影响到程序的性能;
解决思路: 一般长列表加载数据采用两种,一种是分页加载,这种大多用于后台管理系统;另一种是无限上下滚动;很明显无限上下滚动会造成以上问题。最主要的就是解决渲染大量dom节点,因此可以采用虚拟列表,只渲染指定的dom节点,只改变数据的内容和滚动的距离;
知识点: css的transform,节流函数;
示例代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div class="contain">
<div class="contain-area">
<div class="list"></div>
</div>
</div>
</body>
</html>
<script>
// 加载dom节点之后执行loadHtml
document.onload = loadHtml();
// 创建10000条数据的数组
let arr = new Array(10000);
//计算可视区能显示多少个元素
const viewCount = 520 / 52;
let containEle = document.querySelector(".contain");
// 创建渲染区域的第一个元素的索引startIndex和最后一个元素的索引endIndex
// 用于截取列表数据的内容
let startIndex = 0,
endIndex = viewCount - 1;
// 获取10个数据的dom节点
let itemAll = document.querySelectorAll(".list-item");
//滚动事件
// 采用节流不会造成白屏
containEle.onscroll = function () {
// 获取滚动距离
let scrollTop = containEle.scrollTop;
// 获取滚动的元素个数,相当于是第一个元素的索引,向下取整,也可采用Math.ceil
startIndex = Math.floor(scrollTop / 52);
// 获取滚动后最后一个元素的索引
endIndex = 10 + startIndex;
// 截取列表数据的内容,更新之前10条数据的内容
let sliceData = arr.slice(startIndex, endIndex);
for (let index = 0; index < sliceData.length; index++) {
itemAll[index].innerHTML = startIndex + index + 1;
}
// transform移动元素模拟上下滚动加载数据
document.querySelector(".list").style.transform = `translateY(${
startIndex * 52
}px)`;
};
// 更新滚动条滚动条的距离,撑高列表的高度,形成滚动的视觉
document.querySelector(".contain-area").style.height = `${arr.length * 52}px`;
// 渲染10条数据函数
function loadHtml() {
let str = "";
for (let index = 0; index < 10; index++) {
str = str + `<div class="list-item">${index + 1}</div>`;
}
document.querySelector(".list").innerHTML = str;
}
//节流函数 (暂时不用)
function throttle(fn, delay = 500) {
let throttleTimer = null;
let lastTime = 0;
return function () {
let nowTime = new Date();
clearTimeout(throttleTimer);
if (nowTime - lastTime < delay) {
fn.apply(this, arguments);
lastTime = nowTime;
} else {
throttleTimer = setTimeout(() => {
fn.apply(this, arguments);
}, nowTime - lastTime);
}
};
}
</script>
<style>
.contain {
height: 520px;
overflow-y: scroll;
}
.list-item {
margin: 20px;
border: solid 1px black;
text-align: center;
height: 30px;
line-height: 30px;
color: #000;
}
</style>
效果如下: