Element-ui 跑马灯/轮播图 el-carousel 组件虚拟列表实现方案
背景
公司大屏项目有一块地方用到了 el-carousel 组件,但是数据量比较大(3000多条),导致页面卡顿,所以需要对 el-carousel 组件进行优化。
思路方案
利用虚拟列表技术,每次只渲染可视区域的数据,不可视区域的数据不渲染,这样就可以减少渲染的数据量,提高渲染性能。
具体思路是,不论自动轮播还是手动点击指示器,都会触发 el-carousel 的 change 事件,我们可以在 change 事件中获取当前可视区域的数据,然后将数据传给 el-carousel 组件,这样就可以实现虚拟列表的效果。
具体实现
首先,要达到上面的效果,需要配置循环滚动,el-carousel-item
只需要三个就可以。我们把列表变量定义为 visibleList
:
html
<el-carousel
trigger="click"
:interval='5000'
v-show="appList.length !== 0"
:initial-index="0"
@change="pageChange"
indicator-position="none"
ref="carouselRef"
>
<el-carousel-item
v-for="(item, index) in visibleList"
:key="index"
>
<div class="apps">
<App
v-for="(app, ind) in item"
:key="ind"
:item='app'
>
</App>
</div>
</el-carousel-item>
</el-carousel>
然后就是监听切换的动作,显示对应下标的内容。el-carousel
组件中提供 change
事件,事件回传一个 index
参数表示当前页的索引,当前返回的只有0、1、2三个,往后切换的顺序是0 - 1 - 2 - 0,往前切换的顺序是0 - 2 - 1 - 0,了解了切换规律就可以确定是current值是增加还是减少了。代码如下:
vue
<template>
<Container title="应用复制状态监控" center='true'>
<template v-slot:box-content>
<el-carousel
trigger="click"
:interval='5000'
v-show="appList.length !== 0"
:initial-index="0"
@change="pageChange"
indicator-position="none"
ref="carouselRef"
>
<el-carousel-item
v-for="(item, index) in visibleList"
:key="index"
>
<div class="apps">
<App
v-for="(app, ind) in item"
:key="ind"
:item='app'
>
</App>
</div>
</el-carousel-item>
<ul class="el-carousel__indicators el-carousel__indicators--horizontal">
<li class="el-carousel__indicator el-carousel__indicator--horizontal"
v-for="(item, idx) of appList"
:key="idx"
:class="{ 'is-active': idx === currentIdx }"
@click="goToPage(idx)"
>
<button class="el-carousel__button"></button>
</li>
</ul>
</el-carousel>
</template>
</Container>
</template>
<script>
import Container from '../container.vue';
import App from '../application.vue';
export default {
name: 'Footer',
props: {
data: { // 父组件传递的数据
required: true,
type: Object,
default: () => ({})
}
},
data() {
return {
appList: [], // 应用系统列表
currentIdx: 0, // 当前索引
prevIndex: 0, // 前一次索引
toIndex: 0, // 要跳转的下标
customSwiper: false // 用于判断是否自定义的切换
}
},
computed: {
// 用来展示的计算过的轮播列表
visibleList() {
const ls = [];
ls[this.prevIndex] = this.appList[this.currentIdx];
const prev = this.appList[this.currentIdx - 1] || '';
const next = this.appList[this.currentIdx + 1] || '';
if (this.prevIndex === 0) {
ls[1] = next;
ls[2] = prev;
} else if (this.prevIndex === 2) {
ls[1] = prev;
ls[0] = next;
} else {
ls[0] = prev;
ls[2] = next;
}
return ls;
}
},
watch: {
data() {
// 如果后端轮播数据少于24个,就不需要分页;否则24个一组分页
this.appList = this.data.applicationMonitorList.length <= 24
? [this.data.applicationMonitorList]
: this.sliceArray(this.data.applicationMonitorList, 24);
}
},
methods: {
sliceArray(array, chunkSize) {
const result = [];
for (let i = 0; i < array.length; i += chunkSize) {
result.push(array.slice(i, i + chunkSize));
}
return result;
},
pageChange(index, oldIdx) {
// 如果是手动点击轮播图指示器
if (this.customSwipe) {
this.currentIdx = this.toIndex;
this.customSwipe = false;
} else {
// 如果是自动轮播
if (index - this.prevIndex === 1 || index - this.prevIndex === -2) {
// 往后切换
if (this.currentIdx === this.appList.length - 1) {
this.currentIdx = 0;
} else {
++this.currentIdx;
}
}
if (index - this.prevIndex === -1 || index - this.prevIndex === 2) {
// 往前切换
if (this.currentIdx === 0) {
this.currentIdx = this.appList.length - 1;
} else {
--this.currentIdx;
}
}
}
this.prevIndex = index;
},
goToPage(idx) {
// 需要切换的下标大于当前,用next方法模拟
if (this.currentIdx < idx) {
this.$refs.carouselRef.next();
}
// 反之,使用prev方法
if (this.currentIdx > idx) {
this.$refs.carouselRef.prev();
}
this.customSwipe = true; // 用于判断是否自定义的切换
this.toIndex = idx; // 跳转的下标
}
},
components: {
Container,
App
}
}
</script>
<style lang="scss" scoped>
@import '../../../../../assets/css/hatech';
/deep/ .el-carousel--horizontal {
position: absolute;
height: 100%;
}
/deep/ .el-carousel__container {
height: 94%;
}
/deep/ .el-carousel__button {
width: ha-px-vw(25px);
}
.apps {
width: 98%;
height: 100%;
display: flex;
padding: ha-px-vh(20px) ha-px-vw(15px);
justify-content: space-between;
align-content: space-around;
flex-wrap: wrap;
}
.el-carousel {
position: relative;
}
</style>