1、虚拟列表
虚拟列表是一种优化技术,用于在网页上渲染大量的数据,而不影响性能和用户体验。虚拟列表的原理是只渲染可视区域内的数据,而将其他数据隐藏或占位,从而减少 DOM 节点的数量和重绘次数。虚拟列表可以应用于表格、列表、下拉框等场景,提高网页的流畅度和响应速度。
效果图
页面体验:https://demo.foreval.cn/virtualScrolling/
代码
1、html版本
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>虚拟滚动</title>
<style>
.virtualListWrap {
box-sizing: border-box;
width: 240px;
border: solid 1px #000000;
overflow-y: auto;
position: relative;
}
.virtualListWrap .contentList {
width: 100%;
height: auto;
position: absolute;
top: 0;
left: 0;
}
.virtualListWrap .contentList .itemClass {
box-sizing: border-box;
width: 100%;
height: 40px;
line-height: 40px;
text-align: center;
}
.virtualListWrap .contentList .itemClass:nth-child(even) {
background: #c7edcc;
}
.virtualListWrap .contentList .itemClass:nth-child(odd) {
background: pink;
}
.virtualListWrap .loadingBox {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
width: 100%;
height: 100%;
background-color: rgba(255, 255, 255, 0.64);
color: green;
display: flex;
justify-content: center;
align-items: center;
}
</style>
</head>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<body>
<div id="app">
<!--
虚拟列表容器,类似“窗口”,窗口的高度取决于一次展示几条数据
比如窗口只能看到10条数据,一条40像素,10条400像素
故,窗口的高度为400像素,注意要开定位和滚动条
-->
<div class="virtualListWrap" ref="virtualListWrap" @scroll="handleScroll"
:style="{ height: itemHeight * count + 'px' }">
<!-- 占位dom元素,其高度为所有的数据的总高度 -->
<div class="placeholderDom" :style="{ height: allListData.length * itemHeight + 'px' }"></div>
<!-- 内容区,展示10条数据,注意其定位的top值是变化的 -->
<div class="contentList" :style="{ top: topVal }">
<!-- 每一条(项)数据 -->
<div v-for="(item, index) in showListData" :key="index" class="itemClass"
:style="{ height: itemHeight + 'px' }">
{{ item }}
</div>
</div>
<!-- 加载中部分 -->
<div class="loadingBox" v-show="loading">
<i class="el-icon-loading"></i>
<span>loading...</span>
</div>
</div>
</div>
<script>
const { createApp } = Vue
createApp({
data() {
return {
allListData: [], // 所有的数据,比如这个数组存放了十万条数据
itemHeight: 40, // 每一条(项)的高度,比如40像素
count: 10, // 一屏展示几条数据
start: 0, // 开始位置的索引
end: 10, // 结束位置的索引
topVal: 0, // 父元素滚动条滚动,更改子元素对应top定位的值,确保联动
loading: false,
}
},
computed: {
// 从所有的数据allListData中截取需要展示的数据showListData
showListData: function () {
return this.allListData.slice(this.start, this.end);
},
},
async created() {
this.loading = true;
this.allListData = Array.from({ length: 100000 }, (v, i) => `第 ${i + 1} 条数据`);
this.loading = false;
},
methods: {
// 滚动这里可以加上节流,减少触发频次
handleScroll() {
/**
* 获取在垂直方向上,滚动条滚动了多少像素距离Element.scrollTop
*
* 滚动的距离除以每一项的高度,即为滚动到了多少项,当然,要取个整数
* 例:滚动4米,一步长0.8米,滚动到第几步,4/0.8 = 第5步(取整好计算)
*
* 又因为我们一次要展示10项,所以知道了起始位置项,再加上结束位置项,
* 就能得出区间了【起始位置, 起始位置 + size项数】==【起始位置, 结束位置】
* */
const scrollTop = this.$refs.virtualListWrap.scrollTop;
this.start = Math.floor(scrollTop / this.itemHeight);
this.end = this.start + this.count;
/**
* 动态更改定位的top值,确保联动,动态展示相应内容
* */
this.topVal = this.$refs.virtualListWrap.scrollTop + "px";
},
},
}).mount('#app')
</script>
</body>
</html>
2、template版本
<template>
<!-- 虚拟列表容器,类似“窗口”,窗口的高度取决于一次展示几条数据
比如窗口只能看到10条数据,一条40像素,10条400像素
故,窗口的高度为400像素,注意要开定位和滚动条 -->
<div
class="virtualListWrap"
ref="virtualListWrap"
@scroll="handleScroll"
:style="{ height: itemHeight * count + 'px' }"
>
<!-- 占位dom元素,其高度为所有的数据的总高度 -->
<div
class="placeholderDom"
:style="{ height: allListData.length * itemHeight + 'px' }"
></div>
<!-- 内容区,展示10条数据,注意其定位的top值是变化的 -->
<div class="contentList" :style="{ top: topVal }">
<!-- 每一条(项)数据 -->
<div
v-for="(item, index) in showListData"
:key="index"
class="itemClass"
:style="{ height: itemHeight + 'px' }"
>
{{ item }}
</div>
</div>
<!-- 加载中部分 -->
<div class="loadingBox" v-show="loading">
<i class="el-icon-loading"></i>
<span>loading...</span>
</div>
</div>
</template>
<script>
import axios from "axios";
export default {
data() {
return {
allListData: [], // 所有的数据,比如这个数组存放了十万条数据
itemHeight: 40, // 每一条(项)的高度,比如40像素
count: 10, // 一屏展示几条数据
start: 0, // 开始位置的索引
end: 10, // 结束位置的索引
topVal: 0, // 父元素滚动条滚动,更改子元素对应top定位的值,确保联动
loading: false,
};
},
computed: {
// 从所有的数据allListData中截取需要展示的数据showListData
showListData: function () {
return this.allListData.slice(this.start, this.end);
},
},
async created() {
this.loading = true;
this.allListData = Array.from({ length: 100000 }, (v, i) => `第 ${i + 1} 条数据`);
this.loading = false;
},
methods: {
// 滚动这里可以加上节流,减少触发频次
handleScroll() {
/**
* 获取在垂直方向上,滚动条滚动了多少像素距离Element.scrollTop
*
* 滚动的距离除以每一项的高度,即为滚动到了多少项,当然,要取个整数
* 例:滚动4米,一步长0.8米,滚动到第几步,4/0.8 = 第5步(取整好计算)
*
* 又因为我们一次要展示10项,所以知道了起始位置项,再加上结束位置项,
* 就能得出区间了【起始位置, 起始位置 + size项数】==【起始位置, 结束位置】
* */
const scrollTop = this.$refs.virtualListWrap.scrollTop;
this.start = Math.floor(scrollTop / this.itemHeight);
this.end = this.start + this.count;
/**
* 动态更改定位的top值,确保联动,动态展示相应内容
* */
this.topVal = this.$refs.virtualListWrap.scrollTop + "px";
},
},
};
</script>
<style scoped lang="less">
// 虚拟列表容器盒子
.virtualListWrap {
box-sizing: border-box;
width: 240px;
border: solid 1px #000000;
// 开启滚动条
overflow-y: auto;
// 开启相对定位
position: relative;
.contentList {
width: 100%;
height: auto;
// 搭配使用绝对定位
position: absolute;
top: 0;
left: 0;
.itemClass {
box-sizing: border-box;
width: 100%;
height: 40px;
line-height: 40px;
text-align: center;
}
// 奇偶行改一个颜色
.itemClass:nth-child(even) {
background: #c7edcc;
}
.itemClass:nth-child(odd) {
background: pink;
}
}
.loadingBox {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
width: 100%;
height: 100%;
background-color: rgba(255, 255, 255, 0.64);
color: green;
display: flex;
justify-content: center;
align-items: center;
}
}
</style>
2、虚拟表格
推荐一个好用的UI组件,vxetable,看名字就知道做的是表格相关的业务。其中就包括虚拟列表。
vue2和vue3版本都支持,性能比较好,官方说:虚拟滚动(最大可以支撑 5w 列、30w 行)
强大!
官方网站地址:vxetable.cn/v3/#/table/…
这里不做过多介绍,有兴趣可以移至官网查看
评论区