当数据很多时,比如一万条时,如果只用v-for的话那么会渲染出一万个dom元素,这样是很消耗性能的,这时我们便可以使用虚拟列表了。
那什么是虚拟列表呢?
顾名思义,就是一个虚假的列表,可能它里面只渲染了20个dom元素,但是却可以通过滚动来展示一万条数据。
这里封装了一个简单的虚拟列表的组件,里面包含了 rowHeight:行高、viewCount:显示条数、listItem:每一行显示的内容、listData:需要渲染的整体数据,另外还添加了一个点击向父组件传递信息的方法 onClick,实现效果如下:
具体实现方法:
1.写个组件,如下:
<template>
<div class="viewPort" ref="viewPort" :style="{'--scroll-eight':rowHeight+'px'}" @scroll="handleScroll">
<div class="scrollBar" ref="scrollBar"></div>
<div class="list" ref="list">
<div class="row" ref="row" v-for="(item,index) in finalList" :key="index" @click="handleClick(item)">
{{ listItem?item[listItem]:item }}
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
startIndex:0, // 开始索引默认为0
endIndex:this.viewCount, // 结束索引为列表展示条数
};
},
props:{
// 行高
rowHeight:{
type:Number,
default:20,
},
// 显示条数
viewCount:{
type:Number,
default:20,
},
// 显示内容
listItem:{
type:String,
required:true
},
// 数据
listData:{
type:Array,
required:true
}
},
computed:{
finalList(){
return this.listData.slice(this.startIndex,this.endIndex); // 展示的数据
}
},
mounted() {
this.$refs.viewPort.style.height = this.$refs.list.style.height = this.rowHeight*this.viewCount + "px"; // 视口高度动态赋值
this.$refs.scrollBar.style.height = (this.listData.length * this.rowHeight) + "px"; // 滚动条高度动态赋值
},
methods: {
handleScroll(){
this.startIndex = Math.round(this.$refs.viewPort.scrollTop/this.rowHeight); // 开始索引
this.endIndex = this.startIndex + this.viewCount; // 结束索引
this.$refs.list.style.transform = `translateY(${this.startIndex*this.rowHeight}px)`; // 向下平移
},
handleClick(e){
this.$emit("onClick",e); // 绑定onClick事件,向调用者传递数据
}
},
};
</script>
<style scoped lang="scss">
.viewPort{
width: 100%;
overflow-y: auto;
position: relative;
background-color: #ff0;
.list{
position: absolute;
top: 0;
left: 0;
width: 100%;
.row{
height: var(--scroll-eight);
width: 100%;
&:hover{
background-color: #eee;
color: #f0f;
}
&:active{
background-color: #eee;
color: #f0f;
}
}
}
}
</style>
2.在需要使用的页面引入组件
<template>
<div>
<v-list :list-item="title" :list-data="listData" @onClick="onClick" :view-count="10" :row-height="15"></v-list>
</div>
</template>
<script>
import VList from './vList.vue'
export default {
components:{VList},
data() {
let arrList = new Array(10000).fill(null).map((item,index)=>{return {title: index+1}});
return {
listData:'title',
listData:Object.freeze(arrList)
};
},
methods: {
onClick(e){
console.log(e,"<=========");
}
},
};