列表数据过多会存在什么问题
- 数据过多渲染时间很长
- dom节点过多造成页面卡顿
解决方案
1 分片加载
分片加载的原理 将一次js执行时间+ 一次渲染时间,优化成 n 次 js执行间, 存在 n 次渲染
<div id="container"></div>
var container = document.querySelector('#container')
// 记录加载位置
var index = 0
// 每次加载1000 条数据
function loadData() {
// 限制加载总数
if(index>= 500000){
return
}
// 渲染数据
for(var i=1; i<= 1000, i++) {
var div = document.createElement('div')
div.innnerText = index + i
}
// 数据变化
index+= 1000
// 加载下个分片
// loadData() 瞬间加载,错误写法
setTimeout(loadData(),0) // 在下一次宏任务中进行加载,正确写法
}
为什么不能直接调用loadData方法 直接加载,而要使用setTimeout来加载数据?
因为js 是单线程的,主线程只有一个(能操作DOM的),线程有 js引擎线程:执行js代码、GUI 渲染线程:渲染页面,事件、定时器、Ajax 是单独线程,最后还有Eventloop线程。 当一轮eventloop结束后 js引擎线程代码执行完毕以后就去渲染页面(执行GUI渲染线程),在渲染完毕后,从宏任务队列中取出第一个任务进行执行。所以我们不能直接调用loadData方法,而是要重新开一个宏任务。
2 虚拟列表 虚拟列表加载原理,只对屏幕可视区域进行渲染,对不可见区域的数据不渲染或者部分渲染。
<template>
<div class="container scroll-touch" ref="container" :style="{height:containerHeight + 'px'}" @scroll="handleScroll">
<div class="list" :style="{height:totalHeight, paddingTop:top}">
<div class="list-item"
:style="{height:itemHeight + 'px'}"
v-for="item in shownData" :key="item.id">
{{item.content}}
</div>
</div>
</div>
</template>
<script>
export default {
name:'VirtualScrollList',
data() {
return {
start:0, //显示数组的开始下标
end: this.pageSize * 3, // 显示数组的结束下标
}
},
props:{
// 一页显示多少条数据
pageSize:{
type:Number,
default:10
},
// 所有列表数据
items: {
type:Array,
required:true
},
// 一列列表的高度
itemHeight: {
type:Number,
required:true
}
},
computed:{
shownData() {
return this.items.slice(this.start,this.end)
},
totalHeight() {
return this.items.length * this.itemHeight + 'px'
},
containerHeight() {
return this.itemHeight * this.pageSize
},
top() {
return this.start * itemHeight + 'px'
}
},
methods: {
handleScroll() {
const scrollTop = this.$refs.container.scrollTop
const currentPageSize = Math.floor(scrollTop / this.containerHeight)
//当滑动到倒数1、2、3页面的时候不去改变数组下标
if((this.pageSize * currentPageSize+ (this.pageSize * 3)<= this.items.length)) {
this.start = this.pageSize * currentPageSize
this.end = this.start + (this.pageSize * 3)
}
}
}
}
</script>
<style scoped>
.container {
overflow-y:auto;
width:500px;
margin:auto;
background:#f1f1f1;
}
.list {
width:100%;
box-sizing: border-box;
}
.list-item {
display: flex;
justify-content: center;
align-items: center;
border-bottom:1px solid #d3d3d3;
box-sizing:border-box;
}
.scroll-touch {
-webkit-overflow-scrolling: touch;
}
.scroll-touch::-webkit-scrollbar {
width: 10px;
}
.scroll-touch::-webkit-scrollbar-thumb {
border-radius: 0;
background: rgba(0,0,0,.6);
}
.scroll-touch::-webkit-scrollbar-track {
background: #f4f4f4;
}
</style>
至此我们已经完成一个虚拟列表,下面让我们看看效果