固定宽高虚拟滚动列表是一种前端性能优化技术,用于在渲染大量列表数据时,只渲染可视区域内的元素,而不是一次性渲染全部数据。这种技术显著减少了DOM元素数量和计算开销,从而提升页面性能和用户体验。
实现原理
虚拟滚动列表的核心思想是“只渲染用户能看到的那部分”。具体来说,虚拟列表将长列表划分为三个区域:上缓冲区、可视区和下缓冲区。当用户滚动时,只有可视区内的元素会被渲染和更新,而上缓冲区和下缓冲区的元素则不会被渲染或更新,从而减少计算和渲染的开销
实现步骤
- 定义固定宽高的容器:创建一个固定宽高的容器作为视口容器元素,限制无限虚拟列表的可视区域大小。
- List item创建可滚动区域:在视口容器内创建一个可滚动区域,该区域的宽度和高度为父元素的100%,纵向超出部分可滚动。
- 内容区域渲染:在内容区域内渲染部分列表项,这些列表项的高度是固定的。当用户滚动时,动态改变可视区域内的渲染内容,只更新可视区内的元素
实现代码
父页面
<template>
<div class="container" ref="container">
<VirtualList :items="tableData" :itemHeight="60" :showItemsAmount="8"></VirtualList>
</div>
</template>
<script>
import VirtualList from "@/components/VirtualList.vue";
export default {
components:{
VirtualList
},
data() {
return {
tableData: []
}
},
created () {
for (let i = 0; i < 1000000; i++) {
this.tableData.push({
id: i,
content: `这是第${i+1}条数据`
})
}
},
methods: {
}
}
</script>
<style scoped>
</style>
虚拟滚动列表组件
<template>
<div class="container" :style="{height: containerHeight}" ref="container" @scroll="handleScroll">
<div class="list-container" :style="{top: itemTop}">
<!-- 列表元素 -->
<div class="list-item" v-for="item in showItems" :key="item.id" :style="{ height: itemHeight+ 'px' }">
{{item.content}}
</div>
<!-- 真实的dom,完成撑开 -->
<div class="act-container" :style="{height: actContainerHeight}"></div>
</div>
</div>
</template>
<script>
export default {
name: "VirtualList.vue",
props:{
// 列表数据
items: {
type: Array,
default: () => []
},
// 列表元素高度
itemHeight: {
type: Number,
default: () => 60
},
// 列表元素展示个数
showItemsAmount: {
type: Number,
default: () => 5
}
},
data () {
return {
startIndex: 0, // 要展示的数据的起始下标
endIndex: this.showItemsAmount // 要展示的数据的结束下标
}
},
computed:{
// 最终筛选出的要展示的数据
showItems () {
return this.items.slice(this.startIndex, this.endIndex);
},
// 列表向上滚动时要动态改变 top 值
itemTop() {
return this.startIndex * this.itemHeight + 'px';
},
// 容器的高度
containerHeight() {
return this.itemHeight * this.showItemsAmount + 'px';
},
// 撑开容器内容高度的元素的高度
actContainerHeight() {
return this.itemHeight * this.items.length + 'px';
},
},
methods:{
handleScroll () {
// 获取container容器滚动条到顶部的距离
const scrollTop = this.$refs.container.scrollTop;
// 计算卷去的数据条数,用计算的结果作为获取数据的起始和结束下标
// 起始的下标就是卷去的数据条数,向下取整
this.startIndex = Math.floor(scrollTop/this.itemHeight);
// 结束的下标就是起始的下标加上要展示的数据条数
this.endIndex = this.startIndex + this.showItemsAmount;
}
}
}
</script>
<style lang="scss" scoped>
.container {
background-color: #286f8d;
position: relative;
overflow-y: scroll;
font-size: 20px;
line-height: 60px;
text-align: center;
.list-container {
position: absolute;
top: 0;
width: 100%;
}
}
</style>