当下拉选择器含大量的数据时,影响性能问题,因而以懒加载的方式作为性能优化;
该下拉选择器包含功能:物理搜索、触底懒加载、适用单选和多选;
其中要点:
<template>
<yh-select v-model="selectVal" placeholder="请选择" filterable clearable :data-class="className" :popper-class="className" v-selectScroll="scrollChange" :filter-method="filtereMethod" :multiple="multiple" collapse-tags collapse-tags-tooltip :reserve-keyword="false" @change="changeVal">
<yh-option v-for="(item, index) in selectParams.showData" @click="clickOption(item.value)" :key="index" :value="item.value" :label="item.label" :title="item.label" />
</yh-select>
</template>
const props = defineProps({
value: { default: null },
optionsData: { default: [] },
className: { default: 'lazyselect' },
multiple: { default: false },
});
const emit = defineEmits(['returnValue']);
let selectVal = ref();
//默认下拉的分页数
let defaultPageNum = ref(200);
//下拉参数
let selectParams = ref({
pageSize: 1,//当前页码
pageNumber: defaultPageNum.value,//默认显示条数
allData: [],//全部数据
showData: [],//展示数据
activeFilter: false,//搜索状态
});
//下拉选中值监听
watch(
() => props.value,
(to, from) => {
selectVal.value = to;
addValueSelect();
}
);
onMounted(() => {
selectParams.value = {
pageSize: 1,
pageNumber: defaultPageNum.value,
total: 0,
allData: props.optionsData || [],
showData: props.optionsData?.length ? props.optionsData?.slice(0, props.optionsData.length < defaultPageNum.value ? props.optionsData.length : defaultPageNum.value) : [],
activeFilter: false,
};
addValueSelect();
selectVal.value = props.multiple ? selectVal.value?.split(',') || [] : selectVal.value || '';
});
//点击选项,非多选时执行
const clickOption = (e) => {
if (!props.multiple) {
selectParams.value.activeFilter = false;
selectVal.value = e;
emit('returnValue', selectVal.value);
}
};
//添加已选的下拉选项
const addValueSelect = () => {
const value = selectVal.value;
if (![null, undefined].includes(value) && value !== '' && !selectParams.value.showData?.find((i) => i.value == value) && selectParams.value.allData.length) {
const item = selectParams.value.allData.find((i) => i.value === value);
if (item) {
selectParams.value.showData.push(item);
}
}
};
//滚动下拉
const scrollChange = () => {
const { allData, showData, pageNumber } = selectParams.value;
if (showData.length < allData.length && !selectParams.value.activeFilter) {
selectParams.value.pageSize += 1;
const addData = selectParams.value.pageSize * pageNumber;
selectParams.value.showData = addData > allData.length ? allData : allData.slice(0, addData);
}
addValueSelect();
};
//下拉搜索
const filtereMethod = (e) => {
const { allData, showData } = selectParams.value;
if (e !== '') {
if (!props.multiple) {
selectVal.value = e;
}
const filterArr = allData.filter((i) => i.label.toLowerCase().includes(e?.toLowerCase()));
selectParams.value.showData = filterArr;
selectParams.value.activeFilter = true;
} else {
selectParams.value.showData = reSetOptions(allData.slice(0, defaultPageNum.value).concat(showData), 'value');
if (selectParams.value.activeFilter) {
changeVal(e);
}
selectParams.value.activeFilter = false;
}
};
//数组去重
const reSetOptions = (arr, name) => {
let obj = {};
return arr.reduce((cur, next) => {
obj[next[name]] ? '' : (obj[next[name]] = true && cur.push(next));
return cur;
}, []);
};
//下拉值修改
const changeVal = (e) => {
selectParams.value.activeFilter = false;
selectVal.value = e;
emit('returnValue', selectVal.value);
};
代码要点解析:
select触底指令:
import type { Directive, DirectiveBinding } from 'vue';
//下拉滚动触底监听指令
const selectScroll: Directive = {
mounted(el, binding) {
const className = `.${el.dataset.class}`, element = document.querySelector(className)?.querySelector(".el-select-dropdown .el-select-dropdown__wrap");
element?.addEventListener("scroll", () => {
scrollSelect(element, binding);
})
},
beforeUnmount(el, binding) {
const className = `.${el.dataset.class}`, element = document.querySelector(className)?.querySelector(".el-select-dropdown .el-select-dropdown__wrap");
element?.removeEventListener("scroll", () => {
scrollSelect(element, binding);
})
}
}
const scrollSelect = (element, binding) => {
const { scrollTop, scrollHeight, clientHeight } = element,
scrollDistance = scrollHeight - scrollTop - clientHeight;
if (scrollDistance <= 0) {
binding.value();
}
}