背景
在项目开发中,有时候我们会遇到数据量大,搜索框加载缓慢甚至数据量达到一定程度时会出现卡顿甚至卡死的现象,这时候为了解决这一问题通常会使用到虚拟列表方法。
思想
每次只渲染自己自定义的数量,通过滚动条的变化来加载每一轮的数据。
官网地址:https://tangbc.github.io/vue-virtual-scroll-list/#/
操作
安装插件:
npm install vue-virtual-scroll-list --save
使用
- father.vue文件
<SelectCode
ref="Select"
:selectData="selectData"
v-model="defaultValue"
placeholder="请选择"
clearable
></SelectCode>
defaultValue: "",
selectData: {
data: [], // 下拉框数据
label: "label", // 下拉框需要显示的名称
value: "key", // 下拉框绑定的值
isRight: true, //右侧是否显示
},
- selectCode.vue文件
<el-select
:value="defaultValue"
popper-class="virtualselect"
filterable
:filter-method="filterMethod"
@visible-change="visibleChange"
v-bind="$attrs"
v-on="$listeners"
style="width: calc(100% - 24px)"
:popper-append-to-body="false"
>
<virtual-list
ref="virtualList"
class="virtualselect-list"
:data-key="selectData.value"
:data-sources="selectArr"
:data-component="itemComponent"
:keeps="20"
:extra-props="{
label: selectData.label,
value: selectData.value,
isRight: selectData.isRight,
}"
></virtual-list>
</el-select>
<script>
import virtualList from "vue-virtual-scroll-list";
import ElOptionNode from "./el-option-node";
export default {
components: {
"virtual-list": virtualList,
},
model: {
prop: "defaultValue",
event: "change",
},
props: {
selectData: {
type: Object,
default() {
return {};
},
}, //父组件传的值
defaultValue: {
type: String,
default: "",
}, // 绑定的默认值
},
mounted() {
this.init();
},
watch: {
"selectData.data"() {
this.init();
},
},
data() {
return {
itemComponent: ElOptionNode,
selectArr: [],
};
},
methods: {
init() {
if (!this.defaultValue) {
this.selectArr = this.selectData.data;
} else {
// 回显问题
// 由于只渲染20条数据,当默认数据处于20条之外,在回显的时候会显示异常
// 解决方法:遍历所有数据,将对应回显的那一条数据放在第一条即可
this.selectArr = JSON.parse(JSON.stringify(this.selectData.data));
let obj = {};
for (let i = 0; i < this.selectArr.length; i++) {
const element = this.selectArr[i];
if (
element[this.selectData.value].toLowerCase() ===
this.defaultValue.toLowerCase()
) {
obj = element;
this.selectArr.splice(i, 1);
break;
}
}
this.selectArr.unshift(obj);
}
},
// 搜索
filterMethod(query) {
if (query !== "") {
this.$refs.virtualList.scrollToIndex(0); //滚动到顶部
setTimeout(() => {
this.selectArr = this.selectData.data.filter((item) => {
return this.selectData.isRight
? item[this.selectData.label]
.toLowerCase()
.indexOf(query.toLowerCase()) > -1 ||
item[this.selectData.value]
.toLowerCase()
.indexOf(query.toLowerCase()) > -1
: item[this.selectData.label]
.toLowerCase()
.indexOf(query.toLowerCase()) > -1;
});
}, 100);
} else {
this.init();
}
},
visibleChange(bool) {
if (!bool) {
this.$refs.virtualList.reset();
this.init();
}
},
},
};
添加样式:
.virtualselect {
// 设置最大高度
&-list {
max-height: 245px;
overflow-y: auto;
}
/deep/ .el-scrollbar .el-scrollbar__bar.is-vertical {
width: 0;
}
}
- ElOptionNode.vue文件
<template>
<el-option :key="label + value" :label="source[label]" :value="source[value]">
<span>{{ source[label] }}</span>
</el-option>
</template>
<script>
export default {
name: "item-component",
props: {
index: {
type: Number,
}, // 每一行的索引
source: {
type: Object,
default() {
return {};
},
}, // 每一行的内容
label: {
type: String,
}, // 需要显示的名称
value: {
type: String,
}, // 绑定的值
isRight: {
type: Boolean,
default() {
return false;
},
}, // 右侧是否显示绑定的值
},
};
</script>
总结
使用虚拟列表能解决el-select遇到的数据量大的问题,更多使用方法请参考官网介绍。