创建一个十万条数据的接口
这个自己用mock模拟或者自己用nodejs的express直接写一下就是核心代码如下
js
route.get("/bigData", (req, res) => {
res.header('Access-Control-A1low-origin', "*");
//允许時域
let arr=[] // 定义数组,存放十万条数据
for(let i = 0; i < 10000; i++){ /循环添加十万条数据
arr.push({
id: i+1,
name: '名字' + (i + 1),
value: i + 1,
})
res.send({ code: 0, msg: "成功", data: arr } //将十万茶数据返回
})
这里贴出后端的接口 http://124.223.69.156:3300/bigData
第一种直接渲染十万条数据
js
async load() {
this.loading = true;
const res = await axios.get("http://ashuai.work: 1000/bigData");
this.arr = res.data.data;
this.loading = false;
}
这种肯定是不行的会导致页面直接卡死
第二种使用定时器分组分批分堆依次渲染 (定时加载分堆的思想)
- 前端请求到十万条数据以后,先不着急渲染,先将10万条数据分堆分批次
- 例如一堆放10条数据,那么十万条就有一万堆
- 使用定时器,一次渲染一堆,渲染一万次就可以了
js
// 分堆
for (let i=0;i < twoArr.length;i++){
setTimeout(()=>{
this.arr = [ ...this.arr, ...twoArr[i]]
},20*i)
}
使用requestAnimationFrame 进行优化
解释 比如浏览器每16.7ms刷新一次,动画回调也每167ms调用一一次,这样就不会存在过度绘制的问题,动画不会掉帧,自然流畅。
js
// 1. 对数据进行分堆处理
let twoArr = this.averageFn(res,data . data)
// 2. 定义一个泣染函数
const useTwoArr = (page)=> {
if(page > twoArr.length){
return
}
// 3. 用动画请求帧来优化
requestAnimationFrame(()=>{
// 4. 取出一项,拼接一项
this.arr = [...this.arrs, ...twoArr[page]]
// 5. 这项进行下项
page = page + 1
// 6. 递归调用这个图数
useTwoArr(page)
})
}
表格滚动触底加载(滚动到底,再加载一堆)
js
import elTableInfinitescroll from "el-table- infinite-scroll";
vue.use(elTableInfinitescroll);
js
async load() {
console.1og("自动多次执行之,肖次执行会根据高度去计算要执行几次合适");
// 第1步,触底加载相当于把二维数組的每一项收出来用,用光时return停止即可
if (this.allTableData.length == 0) {
console.log("没数据啦");
return;
}
//第二步,加载的时候,把二维数组的第一项收出来,拼接到要展示的表格数据中去
let arr = this.allTableData[0];
this.tableData = this.tableData.concat(arr);
console.log(this.tableData);
// 第三步,拼接展示以后,再把2維数组的第一项的数据删除即可
this.allTableData.shift();
使用无限加载/虚拟列表进行展示
什么是虚拟列表?
所谓的虚拟列表实际上是前端障眼法的一种表现形式。 看到的好像所有的数据都渲染了,实际上.只渲染「可视区域」的部分罢了
有点像我们看电影,我们看的话,是在块电影屏幕上,一秒秒的看(不停的放映)
但是实际上电影有俩小时,如果把两个小时的电影都铺开的话,那得需要多少块电影屏幕呢?
同理,如果10万条数据都渲染,那得需要多少dom节点元素呢?
所以我们只给用户看,他当下能看到的
如果用户要快进或快退(下拉滚动条或者上拉滚动条)
再把对应的内容呈现在电影屏幕上(呈现在可视区域内)
这样就实现了看着像是所有的dom元素每一条 数据都有渲染的障眼法效果
基本思路
- 写一个代表可视区域的div容器
- 计算可视区域可以显示的数据条数(可视区域的高度/单条数据的高度)
- 监听滚动,当滚动条变化时,计算被卷起的高度(scrollTop)
- 计算可视区域的起始下标(卷起的高度/单条数据的高度)
- 计算可视区域的结束下标(起始下标+可视区域可以显示的数据条数)
- 取起始下标到结束下标之间的数据,渲染到页面
- 计算起始下标的数据在整个列表中的偏移位置并设置到列表上(transform:translateY())
代码实现
html
<!DOCTYPE html>
<html>
<head>
<title>js 实现虚拟列表</title>
</head>
<style>
html,
body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
ul,
li {
margin: 0;
padding: 0;
list-style: none;
}
.box {
width: 750px;
height: 900px;
overflow-y: auto;
box-sizing: border-box;
margin: 0 auto;
}
.content-area {
overflow: hidden;
}
li {
height: 100px;
background-color: #eee;
margin-bottom: 10px;
display: flex;
align-items: center;
padding-left: 20px;
border-radius: 12px;
box-sizing: border-box;
}
</style>
<body>
<!-- 可视区域的高度--用户看见的高度(750px) -->
<div class="box">
<!-- 内容真实高度 即200条数据的高度-->
<div class="content-area" id="contentArea">
<ul></ul>
</div>
</div>
<script>
let el = document.querySelector(".box");
let itemHeight = 110; //每个元素的高度(li的高度100 + marginBottom 10)
let pageSize = Math.ceil(el.clientHeight / itemHeight); // 获取一个滚动屏最大可容纳子元素个数(向上取整)
let data = []; //mock数据
let startIndex = 0; //可视区第一行下标
let endIndex = pageSize; //可视区最后一行下标
// 初始化模拟数据
let getData = () => {
for (let i = 0; i < 200; i++) {
data.push({
content: `我是显示的内容${i + 1}`,
});
}
};
// 加载数据并插入到dom页面
let loadData = () => {
let html = "";
let sliceData = data.slice(startIndex, endIndex);
for (let i = 0; i < sliceData.length; i++) {
html += `
<li class="item">
<p>${sliceData[i].content}</p>
</li>`;
}
el.querySelector("#contentArea ul").innerHTML = html;
};
// 更新DOM
let updateHtml = () => {
let sliceData = data.slice(startIndex, endIndex);
let itemAll = el.querySelectorAll(".item");
for (let i = 0; i < sliceData.length; i++) {
itemAll[i].querySelector("p").innerHTML = sliceData[i].content;
}
};
// 滑动监听
el.addEventListener("scroll", function () {
let scrollTop = el.scrollTop; // 滚动高度
startIndex = Math.ceil(scrollTop / itemHeight); // 重新计算开始的下标,div顶部卷起来的长度除以列表元素的高度
endIndex = startIndex + pageSize;
updateHtml(); // 重新更新dom
el.querySelector("#contentArea ul").style.transform =
"translateY(" + startIndex * itemHeight + "px)";
});
let init = () => {
getData();
loadData();
document.getElementById("contentArea").style.height =
itemHeight * data.length + "px"; // 占位dom的高度
};
// 页面初始化调用
init();
</script>
</body>
</html>