理论
当我们有大量数据需要渲染时,会照成页面的卡顿,这是由于页面的dom太多照成的,我我们可以使用虚拟列表来优化显示。
虚拟列表的核心实现有两部分,
- 可滚动区域:假设有 1000 条数据,每个列表项的高度是 30,那么可滚动的区域的高度就是 1000 * 30。当用户改变列表的滚动条的当前滚动值的时候,会造成可见区域的内容的变更。
- 可见区域:比如列表的高度是 300,右侧有纵向滚动条可以滚动,那么视觉可见的区域就是可见区域。
实现虚拟列表就是处理滚动条滚动后的可见区域的变更,其中具体步骤如下:
计算当前可见区域起始数据的 startIndex
计算当前可见区域结束数据的 endIndex
计算当前可见区域的数据,并渲染到页面中
计算 startIndex 对应的数据在整个列表中的偏移位置 startOffset,并设置到列表上;
微信图片_20220823111909.jpg
使用vue的一个简单实现
<!-- 虚拟列表 -->
<template>
<div class="list-view" @scroll="handleScroll">
<div
class="list-view-phantom"
:style="{
height: contentHeight,
}"
></div>
<div ref="content" class="list-view-content">
<div
class="list-view-item"
:style="{
height: itemHeight + 'px',
}"
v-for="item in visibleData"
>
{{ item }}
</div>
</div>
</div>
</template>
<script>
export default {
name: "HelloWorld",
props: {
data: {
type: Array,
required: true,
},
itemHeight: {
type: Number,
default: 30,
},
},
computed: {
contentHeight() {
return this.data.length * this.itemHeight + "px";
},
},
mounted() {
this.updateVisibleData();
},
data() {
return {
visibleData: [],
};
},
methods: {
updateVisibleData(scrollTop) {
scrollTop = scrollTop || 0;
const visibleCount = Math.ceil(this.$el.clientHeight / this.itemHeight);
const start = Math.floor(scrollTop / this.itemHeight);
const end = start + visibleCount;
this.visibleData = this.data.slice(start, end);
this.$refs.content.style.webkitTransform = `translate3d(0, ${
start * this.itemHeight
}px, 0)`;
},
handleScroll() {
const scrollTop = this.$el.scrollTop;
this.updateVisibleData(scrollTop);
},
},
};
</script>
<style>
.list-view {
height: 400px;
overflow: auto;
position: relative;
border: 1px solid #aaa;
}
.list-view-phantom {
position: absolute;
left: 0;
top: 0;
right: 0;
z-index: -1;
}
.list-view-content {
left: 0;
right: 0;
top: 0;
position: absolute;
}
.list-view-item {
padding: 5px;
color: #666;
line-height: 30px;
box-sizing: border-box;
}
</style>
网友评论