基于vue的长列表虚拟滚动插件
1 背景
一个长列表 Web 页面,如果需要展示成千上万条数据,那么页面中就会有数万甚至数十万的HTML节点,会巨大的消耗浏览器性能,进而给用户造成非常不友好的体验。
主要体现在以下几个方面:
-
页面等待时间极长,用户体验差;
-
CPU 计算能力不够,滑动会卡顿;
-
GPU 渲染能力不够,页面会跳屏;
-
RAM 内存容量不够,浏览器崩溃。
优化方案:
-
不把长列表数据一次性全部直接显示在页面上;
-
截取长列表一部分数据用来填充屏幕容器区域;
-
长列表数据不可视部分使用使用空白占位填充;
-
监听滚动事件根据滚动位置动态改变可视列表;
-
监听滚动事件根据滚动位置动态改变空白填充。
把上面的优化方案简称为虚拟滚动。
2 介绍
虚拟滚动,就是根据 「 容器可视区域 」 的 「 列表容积数量 」,监听用户滑动或者滚动事件,动态截取 「 长列表数据 」 中的 「 部分数据 」 渲染到页面上,动态使用空白占位填充容器 「上下滚动区域内容 」 ,模拟实现 「 原生滚动效果 」。
3 代码实现
3.1 vue组件实现
VirtualScroll.vue
文件
<template>
<!-- wrapper可视容器需要设置overflow-y:auto;才能监听滚动事件,在父组件使用该组件时,需要设置wrapper可视容器的区域范围 -->
<div class="wrapper" @scroll.passive="scrollHandler" ref="wrapper">
<!-- content填充要显示内容以及上下空白占位 -->
<div class="content" :style="blankFillStyle">
<div v-for="(item, index) in showDataList" :key="index">
<!-- 每条数据的内容结构通过插槽的方式让父组件调用该组件时填充进来 -->
<slot :row="item"></slot>
</div>
</div>
</div>
</template>
<script>
export default {
props:{
// 一条数据内容的高度
oneDataHeight: {
type: Number,
default: 0
},
// 源数据列表
sourceDataList: {
type: Array,
default: () => []
},
// 是否向外触发滚动至底事件(scroll-last)
scrollLastFlag: {
type: Boolean,
default: false
},
// 是否向外触发滚动事件(scroll),会传出滚动的位移
scrollFlag: {
type: Boolean,
default: false
}
},
name: 'VirtualScroll',
data(){
return{
// 可视屏幕容积数量
screenContainSize: 0,
// 当前可视数据起始位置索引
startIndex: 0,
// 滚动事件触发执行的函数
scrollFn: null,
// 保存滚动的位移
scrollY: 0
}
},
mounted(){
this.$nextTick(()=>{
// 挂载后,根据可视容器高度计算可视屏幕容积数量
this.myResize()
// 屏幕尺寸变化以及横屏,都要重新计算可视屏幕容积数量
window.onresize = this.myResize;
window.onorientationchange = this.myResize;
// 通过定时器节流处理生成的函数,用于处理滚动事件
// this.scrollFn = this.throttle(this.setStartIndex, 17);
})
},
methods:{
// 根据可视容器高度计算可视屏幕容积数量
myResize(){
// 两次取反可取整,上下有多余空间,因此需要加2条数据
this.screenContainSize = ~~(this.$refs.wrapper