通过 vue-virtual-scroll-list 库对 element UI 的 Select 组件进行性能优化
前言
今天在开发过程中发现,当下列菜单的项过多时(大于10000条),页面会有明显的卡顿。这里记录一下通过 vue-virtual-scroll-list
对下拉菜单进行性能优化。
项目中使用的是 element ui 组件库,下面是基于element ui进行封装的,其他组件库的封装都大同小异,这里就不赘述了。
这里实现了长列表的优化,也实现了长列表的搜索功能,毕竟如果是几万条数据,不通过搜索根本无法查找数据。
代码实现
// VirtualSelect.vue
<template>
<div class="virtual-select">
<el-select
v-modle="myValue"
:placeholder="placeholder"
filterable
clearable
remote
:remote-method="getFilterData"
@visible-change="visibleOption">
<virtual-list
ref="virtualList"
class="virtual-list"
:data-key="primaryKey"
:data-sources="filterData"
:data-component="componentItem"
/>
</el-select>
</div>
</template>
<script>
import VirtualList from 'vue-virtual-scroll-list'
export default {
components: {
VirtualList
},
props: {
value: {
default: () => {}
},
dataList: {
type: Array,
default: () => []
},
primaryKey: {
type: String,
default: ''
},
searchKey: {
type: Array,
default: () => []
},
placeholder: {
type: String,
default: ''
},
componentItem: {
default: ''
}
},
data() {
return {
myValue: '',
data: [],
filterData: []
}
},
watch: {
dataList(val) {
if (val) {
this.data = val
this.filterData = val
}
},
value(val) {
this.myValue = val
},
myValue(val) {
this.$emit('input', val)
}
},
methods: {
getFilterData(query) {
if (query !== '') {
this.$refs.virtualList.reset()
this.filterData = this.data.filter(item => {
if(this.searchKey.length > 0) {
const res = this.searchKey.filter(searchVal => {
return item[searchVal].toString().toLowerCase().indexOf(query.toLowerCase()) !== -1
})
return res.length > 0
}
})
} else {
this.filterData = this.data
}
},
visibleOption(bool) {
if (!bool) {
setTimeout(() => { this.filterData = this.data }, 10)
} else {
this.$refs.virtualList.reset()
}
}
}
}
</script>
<style lang="scss">
.virtual-list {
max-height: 235px;
overflow-y: auto;
&::-webkit-scrollbar {
width: 10px;
}
&::-webkit-scrollbar-track {
background: #fff;
border-radius: 10px;
}
&::-webkit-scrollbar-thumb {
background: #e7e7e7;
border-radius: 10px;
}
}
.virtual-select {
.el-select {
.el-select__caret:first-child::before {
content: '\e6e1'
}
.if-focus {
.el-select__caret:first-child {
transform: rotate(0deg);
}
}
}
}
</style>
// VirtualOption.vue
<template>
<el-option
:label="source.name+'-'+source.phone"
:value="source.id"
/>
</template>
<script>
export default {
props: {
source: {
type: Object,
default: () => {}
}
}
}
</script>
使用:
<template>
<virtual-select
v-model="currentData"
:dataList="dataList"
primaryKey="id"
:searchKey="['name', 'phone']"
placeholder="请选择数据"
:componentItem="virtualOption"
/>
</template>
<script>
import VirtualSelect from './components/VirtualSelect.vue'
import VirtualOption from './components/VirtualOption.vue'
export default {
components: { VirtualSelect },
data() {
return {
currentData: null,
dataList: [
{ id: 1, name: '小杜', phone: 13833222233 },
{ id: 2, name: '小王', phone: 18322333322 }
],
virtualOption: VirtualOption
}
}
}
</script>