el-select:
element-ui组件中的select下拉选择组件,支持单选、多选等
虚拟列表:
虚拟列表是一种优化技术,用于处理大型列表。在传统的列表中,当用户滚动到底部时,列表会加载所有的数据,这可能导致性能问题和内存泄漏。虚拟列表通过只加载当前可见的部分数据,而不是整个列表的数据来解决这个问题。当用户滚动到新的区域时,虚拟列表会自动加载新的数据,从而提高性能和响应速度。这种技术通常与虚拟滚动结合使用,可以在保持高性能和流畅性的同时,支持大量的数据呈现。
背景:
在做管理系统,下拉选择框是个必备功能,在实际开发中,下拉数据量是不可控的,el-select虽然有过滤筛选等功能,但是如果数据过大的时候,会导致浏览器卡死,在切换页面时,销毁这个也很耗时,导致页面切换体验性很差,所以需要处理这种情况!
最简单处理方案:
默认下拉是空,只支持过滤筛选,实现:filterMethod,可以手动控制展示的数据量
最终处理方案:
用虚拟列表策略处理,重新封装el-select组件
<template>
<el-select
v-model="selected"
:filter-method="filterMethod"
:value-key="props.key ? props.key : props.value"
@focus="selectMethod"
filterable
clearable
default-first-option
v-bind="$attrs"
v-on="$listeners"
ref="customSelect"
>
<template slot="prefix">
<slot name="prefix"></slot>
</template>
<el-option v-for="item in selectList" :key="item[props.key ? props.key : props.value]" :value="item[props.value]" :label="item[props.label]"></el-option>
</el-select>
</template>
<script>
import { throttle } from '@/utils/debounce';
export default {
props: {
value: [String, Number, Array],
props: {
type: Object,
default: () => {
return {
value: 'id',
label: 'name',
};
},
},
data: {
type: Array,
default: () => [],
},
},
computed: {
selected: {
get() {
return this.value;
},
set(val) {
this.$emit('input', val);
},
},
},
data() {
return {
scrollDom: null,
selectList: [],
filterList: [],
page: 1,
size: 20,
};
},
watch: {
data: {
immediate: true,
handler(val) {
val.length && this.initSelect();
},
},
},
mounted() {
this.scrollDom = this.$refs.customSelect.$refs.scrollbar.$refs.wrap;
this.scrollDom.addEventListener('scroll', throttle(this.handlerScroll, 200));
},
beforeDestroy() {
this.scrollDom.removeEventListener('scroll', throttle(this.handlerScroll, 200));
},
methods: {
handlerScroll() {
// TODO:监听滚动,触发加载
if (this.scrollDom.scrollHeight - this.scrollDom.scrollTop - 1 <= this.scrollDom.clientHeight) {
this.loadData();
}
},
firstLoad() {
// 初始化数据
this.page = 1;
const len = this.filterList.length;
if (len <= this.size) {
this.selectList = this.filterList;
} else {
this.selectList = this.filterList.slice(0, this.size);
}
},
selectMethod() {
let param = this.selected;
if (Object.prototype.toString.call(this.selected) === '[object Array]') {
param = this.selected.join('');
}
if (param) {
// TODO:有值不需要重置查询, 但当组件初始化的时候selected有值回显有问题
} else {
this.filterMethod('');
}
},
filterMethod(str) {
this.filterList = this.data.filter(item => {
return item[this.props.label] && item[this.props.label].indexOf(str) > -1;
});
this.firstLoad();
},
initSelect() {
let param = this.selected;
if (Object.prototype.toString.call(this.selected) === '[object Array]') {
param = this.selected.join('');
}
if (param) {
// 筛选
this.filterList = this.data.filter(item => {
return param.indexOf(item[this.props.value]) > -1;
});
this.selectList = this.filterList;
}
},
loadData() {
if (this.filterList.length <= this.page * this.size) {
// 最后一页
return;
}
++this.page;
const len = this.page * this.size;
if (this.filterList.length <= len) {
this.selectList = this.filterList;
} else {
this.selectList = this.filterList.slice(0, len);
}
},
},
};
</script>