1.延迟渲染:
在数据加载完成后,使用requestAnimationFrame来延迟渲染,这可以在下一次重绘之前进行渲染,提高性能。
定时器分页渲染会出现页面白屏掉帧现象,不推荐使用setTimeout,推荐requestAnimationFrame渲染。
出现白屏掉原因:
-
一是setTimeout的执行时间是不确定的,它属于宏任务,需要等同步代码以及微任务执行完后执行。
-
二是屏幕刷新频率受分辨率和屏幕尺寸影响,而setTimeout只能设置一个固定的时间间隔,这个时间不一定和屏幕刷新时间相同。
requestAnimationFrame的调用频率通常为每秒60次。这意味着我们可以在每次重绘之前更新动画的状态,并确保动画流畅运行,而不会对浏览器的性能造成影响。
setInterval与setTimeout它可以让我们在指定的时间间隔内重复执行一个操作,不考虑浏览器的重绘,而是按照指定的时间间隔执行回调函数,可能会被延迟执行,从而影响动画的流畅度。
获取后端数据的公开接口可直接在浏览器打开访问:http://124.223.69.156:3300/bigData
<template>
<el-table :data="tableData" style="width: 80%" height="480" v-loading="loading">
<el-table-column prop="id" label="id" width="180"> </el-table-column>
<el-table-column prop="name" label="姓名" width="180"> </el-table-column>
<el-table-column prop="value" label="值"> </el-table-column>
</el-table>
</template>
<script>
import { getDataList } from "../api/home";
export default {
data() {
return {
tableData: [],
loading: false,
};
},
methods: {
async getData() {
this.loading = true;
const res = await getDataList();
this.loading = false;
// 1.对数据分堆处理,将10万条数据,分成10条一堆,一共1万堆
const twoarr = this.avergeData(res.data.data);
//2.定时器分页渲染函数
for (let i = 0; i < twoarr.length; i++) {
setTimeout(() => {
this.tableData = [...this.tableData, ...twoarr[i]];
}, 20 * i);
}
},
avergeData(data) {
let i = 0;
let res = [];
while (i < data.length) {
res.push(data.slice(i, i + 10));
i = i + 10;
}
return res;
},
},
mounted() {
this.getData();
},
};
</script>
优化后的代码:
<template>
<el-table :data="tableData" style="width: 80%" height="480" v-loading="loading">
<el-table-column prop="id" label="id" width="180"> </el-table-column>
<el-table-column prop="name" label="姓名" width="180"> </el-table-column>
<el-table-column prop="value" label="值"> </el-table-column>
</el-table>
</template>
<script>
import { getDataList } from "../api/home";
export default {
data() {
return {
tableData: [],
loading: false,
};
},
methods: {
async getData() {
this.loading = true;
const res = await getDataList();
this.loading = false;
// 1.对数据分堆处理,将10万条数据,分成10条一堆,一共1万堆
const twoarr = this.avergeData(res.data.data);
// 2.定义一个渲染函数
const useTwoArr = (page) => {
if (page > twoarr.length - 1) {
return;
}
//3.用动画请求帧来优化
requestAnimationFrame(() => {
this.tableData = [...this.tableData, ...twoarr[page]];
page = page + 1;
//4.递归循环取出每一堆进行渲染
useTwoArr(page);
});
};
useTwoArr(0);
},
avergeData(data) {
let i = 0;
let res = [];
while (i < data.length) {
res.push(data.slice(i, i + 10));
i = i + 10;
}
return res;
},
},
mounted() {
this.getData();
},
};
</script>
2.触底加载:
使用element插件el-table-infinite-scroll,安装el-table-infinite-scroll,全局引用插件
vue2 版本引用:
npm install --save el-table-infinite-scroll@2
import ElTableInfiniteScroll from "el-table-infinite-scroll"
Vue.directive("el-table-infinite-scroll", ElTableInfiniteScroll)
vue3版本引用:
npm install --save el-table-infinite-scroll
import ElTableInfiniteScroll from "el-table-infinite-scroll";
app.use(ElTableInfiniteScroll);
<template>
<el-table
:data="tableData"
style="width: 80%"
height="480"
element-loading-text="拼命加载中"
element-loading-spinner="el-icon-loading"
element-loading-background="rgba(0, 0, 0, 0.8)"
v-loading="loading"
v-el-table-infinite-scroll="load"
>
<el-table-column prop="id" label="id" width="180"> </el-table-column>
<el-table-column prop="name" label="姓名" width="180"> </el-table-column>
<el-table-column prop="value" label="值"> </el-table-column>
</el-table>
</template>
<script>
import { getDataList } from "../api/home";
export default {
data() {
return {
tableData: [],
loading: false,
allTableData: [],
};
},
methods: {
async getData() {
this.loading = true;
const res = await getDataList();
this.loading = false;
// 1.对数据分堆处理,将10万条数据,分成10条一堆,一共1万堆
this.allTableData = this.avergeData(res.data.data);
this.load();
},
avergeData(data) {
let i = 0;
let res = [];
while (i < data.length) {
res.push(data.slice(i, i + 10));
i = i + 10;
}
return res;
},
load() {
//触底加载数据
if (this.allTableData.length === 0) {
//没有数据啦
return;
}
this.tableData = this.tableData.concat(this.allTableData[0]);
this.allTableData.shift();
},
},
mounted() {
this.getData();
},
};
</script>
3.虚拟列表:
<template>
<div ref="list" class="v-scroll" @scroll="scrollEvent($event)">
<div class="infinite-list" :style="{ height: listHeight + 'px' }"></div>
<div class="scroll-list" :style="{ transform: getTransform }">
<div
ref="items"
class="scroll-item"
v-for="item in visibleData"
:key="item.id"
:style="{ height: itemHeight + 'px', lineHeight: itemHeight + 'px' }"
>
{{ item.name }}
</div>
</div>
</div>
</template>
<script>
import { getDataList } from "../api/home";
export default {
data() {
return {
listData: listData,
itemHeight: 60,
//可视区域高度
screenHeight: 600,
//偏移量
startOffset: 0,
//起始索引
start: 0,
//结束索引
end: null
};
},
computed: {
//列表总高度
listHeight() {
return this.listData.length * this.itemHeight;
},
//可显示的列表项数
visibleCount() {
return Math.ceil(this.screenHeight / this.itemHeight);
},
//偏移量对应的style
getTransform() {
return `translate3d(0,${this.startOffset}px,0)`;
},
//获取真实显示列表数据
visibleData() {
return this.listData.slice(
this.start,
Math.min(this.end, this.listData.length)
);
},
},
methods: {
scrollEvent() {
//当前滚动位置
let scrollTop = this.$refs.list.scrollTop;
//此时的开始索引
this.start = Math.floor(scrollTop / this.itemHeight);
//此时的结束索引
this.end = this.start + this.visibleCount;
//此时的偏移量
this.startOffset = scrollTop - (scrollTop % this.itemHeight);
},
async getData() {
const res = await getDataList();
this.listData = res.data.data;
},
},
mounted() {
this.getData();
this.start = 0;
this.end = this.start + this.visibleCount;
},
};
</script>
<style>
.v-scroll {
height: 600px;
width: 400px;
border: 3px solid #000;
overflow: auto;
position: relative;
-webkit-overflow-scrolling: touch;
margin-left: 30px;
}
.infinite-list {
position: absolute;
left: 0;
top: 0;
right: 0;
z-index: -1;
}
.scroll-list {
left: 0;
right: 0;
top: 0;
position: absolute;
text-align: center;
}
.scroll-item {
padding: 10px;
color: #555;
box-sizing: border-box;
border-bottom: 1px solid #999;
}
</style>