问题描述
今天遇到一个需求,需要通过一个下拉框选择工作单位/高校,且支持模糊搜索。
数据库中有2820条数据,如果从下拉框中一次性查出2820条数据,必然可能遇到查询时间过久导致页面卡顿的问题,极大的影响用户体验。
解决方案
于是我思考良久,决定做一个动态加载数据的下拉框。
首先,后端提供一个分页查询的接口,通过设置pageNum和pageSize动态控制每次查询多少条数据显示。分页接口这里不做过多阐述,相信大家都会。
重点来了
因为我们需要从下拉框中动态加载远程数据库数据,因此我们可以通过监听滚轮事件来完成,当滚轮滚动到下拉框底部时,设置一个离底部的距离像素值,监听这个距离,当距离小于我们设置的距离时,触发监听事件,从数据库获取数据并塞到我们的下拉框中。
由于饿了么组件提供的el-select框没有监测鼠标滚轮的属性,因此我们可以自定义一个vue指令用于监听鼠标滚轮事件。
第一步:定义自定义vue指令
directives: {
'el-select-loadmore': {
bind(el, binding) {
// 获取element-ui定义好的scroll盒子 监听滚动条加载数据
const SELECTWRAP_DOM = el.querySelector('.el-select-dropdown .el-select-dropdown__wrap');
const debouncedScroll = debounceWrapper(function () {
const condition = SELECTWRAP_DOM.scrollHeight - SELECTWRAP_DOM.scrollTop - SELECTWRAP_DOM.clientHeight;
if (condition <= 1) {
binding.value()
}
})
SELECTWRAP_DOM.addEventListener('scroll', debouncedScroll);
}
}
}
注意:代码中的debouncedScroll添加了防抖事件,目的是为了防止网络抖动,一次性发送多个请求导致数据列表中有相同的数据。
第二步:定义一个下拉框
<el-select
v-model="workUnit"
filterable
v-el-select-loadmore="handleScroll"
remote
placeholder="请选择高校"
@focus="loadInitialOptions"
:remote-method="fetchUniversities"
:default-first-option="true"
@change="handleUniversityChange"
clearable style="width: 200px">
<el-option v-for="item in universities" :key="item.id" :label="item.workName"
:value="item.workName">
</el-option>
</el-select>
第三步:实现页面逻辑
data() {
return {
universities: [],
totalWork: 0,
workUnitQueryParams: {
pageNum: 1,
pageSize: 10,
workNameLike: ''
}
}
},
methods: {
/* 下拉框动态加载工作单位 */
// 下拉框聚焦时获取初始数据
loadInitialOptions() {
this.fetchUniversities('');
},
// 动态加载时远程获取数据方法
fetchUniversities(value) {
this.workUnitQueryParams.workNameLike = value; // 模糊搜索字段赋值
this.workUnitQueryParams.pageNum = 1; // 查询前将页码置为1
getWorkUnitByPage(this.workUnitQueryParams).then(response => {
this.universities = response.data.list;
this.totalWork = response.data.total; // 记录数据总数,用于滚轮加载数据时做判断
}).catch(error => {
console.error('Error fetching universities:', error);
});
},
// 下拉框值变化时触发
handleUniversityChange(value) {
console.log('Selected University:', value); // 记录一下下拉框中的值,可以不要
},
// 手动触发滚轮加载数据
handleScroll() {
// 当列表长度小于数据总数时执行方法
if (this.universities.length < this.totalWork) {
this.workUnitQueryParams.pageNum += 1; // 每次页码+1,请求后端数据
getWorkUnitByPage(this.workUnitQueryParams).then(response => {
// 将数据拼接在原有数据后
this.universities = this.universities.concat(response.data.list);
this.totalWork = response.data.total;
}).catch(error => {
console.error('Error fetching universities:', error);
});
} else {
return
};
},
/* 下拉框动态加载工作单位 */
}
总结
这个需求的难点在于饿了么提供的select组件没有监听鼠标滚轮事件的属性,因此我们需要手动构造一个函数去让它能够监听到鼠标滚轮事件。同时,由于下拉框是一个比较小的盒子,高度有限,因此当通过滚轮到底部的高度差去判断时,可能因为差值过小而引发抖动问题(一次性发送多个同样的请求),这样会导致数据列表中数据重复,因此手动加个防抖方法。