下拉选择框大量数据优化

预计阅读时间: 4 分钟

业务场景

近期着手开发基于 ElementUI 的后台管理系统,偶然间发现「el-select」下拉选择时候遇到一个问题,当渲染下拉选项的「options」的数据量过多时「本项目中的 数据条目已过万」,就会出现下拉选择器卡顿的情况,尤其是在模糊匹配过滤的情况下,显得十分的卡顿。初始化选择器的时候,也会点击无反应,有时候需要点击多次才可出现 「dialog」 弹窗(本次下拉筛选在弹窗中实现)。 翻阅多篇博客笔记之后,最终找到一个可以解决问题的方案,现将此次优化方案记录成为笔记,以便于日后遇到类似问题的时候便于查阅。

「📷」图示效果

image.png

注:

基于 el-select 的下拉筛选,通过自定义事件来实现模糊搜索匹配。

一共两种方案:

  1. 获取所有数据,通过输入的 关键字 自己对获取的数据进行过滤处理;
  2. 通过输入的 关键字 来动态请求后台接口,通过接口返回的数据来动态渲染下拉选项;

「🍵」代码实现

  • 🌰Vue 组件实例
代码示例
1<template>
2	<div class="app">
3		<el-dialog title="标题" :visible.sync="relatedOpen" :append-to-body="true" width="500px">
4			<el-row :gutter="16">
5				<el-col :span="20">
6					<el-select
7						v-model="value"
8						filterable
9						clearable
10						style="width:100%"
11						placeholder="请选择"
12						:loading="searchLoad"
13						:filter-method="filterMethod"
14						v-el-select-loadmore="loadMore(rangeNumber)"
15						@visible-change="visibleChange"
16					>
17						<el-option
18							v-for="item in options.slice(0, rangeNumber)"
19							:key="item.key"
20							:label="item.value"
21							:value="item.key"
22						></el-option>
23					</el-select>
24				</el-col>
25				<el-col :span="4">
26					<el-button type="primary" @click="submit">确定</el-button>
27				</el-col>
28			</el-row>
29		</el-dialog>
30	</div>
31</template>
  • 🚗「v-el-select-loadmore」为自定义指令封装的数据加载指令,是为了解决和优化 elementUI 下拉选择器加载数据过多出现卡顿问题的。
  • 🚴「filter-method」是下拉选择器的一个自定义属性,可以监听输入的变量,从而依据变量来实现数据的动态获取;
自定义指令
1// 自定义指令
2  directives: {
3    "el-select-loadmore": (el, binding) => {
4      // 获取Element UI定义好的scroll元素
5      const SELECTWRAP_ROM = el.querySelector(".el-select-dropdown .el-select-dropdown__wrap");
6      if (SELECTWRAP_ROM) {
7		// 添加scroll事件
8        SELECTWRAP_ROM.addEventListener("scroll", function() {
9			// 判断滚动
10          const condition = this.scrollHeight - this.scrollTop <= this.clientHeight;
11          condition && binding.value();
12        });
13      }
14    }
15  }
  • 相应的数据函数
数据函数
1export default {
2	data() {
3		return {
4			relatedOpen: false,
5			options: [] /* 选择下拉框的值 */,
6			courseList: [],
7			rangeNumber: 10,
8			searchLoad: false /* 下拉框的loading状态 */,
9			value: "",
10			timer: null
11		};
12	},
13	created() {
14		this.getOptions();
15	},
16	methods: {
17		// 按需加载
18		loadMore(n) {
19			return () => (this.rangeNumber += 5);
20		},
21		// 过滤课件
22		filterMethod(query) {
23			if (this.timer != null) clearTimeout(this.timer);
24			!this.searchLoad && (this.searchLoad = true);
25			this.timer = setTimeout(() => {
26				this.options = !!query
27					? this.courseList.filter(el => el.value.toLowerCase().includes(query.toLowerCase()))
28					: this.courseList;
29				clearTimeout(this.timer);
30				this.searchLoad = false;
31				this.rangeNumber = 10;
32				this.timer = null;
33			}, 500);
34		},
35		// 监听select下拉框的显示和隐藏
36		visibleChange(flag) {
37			// 显示时初始化列表
38			flag && this.filterMethod("");
39			// 初始化默认值
40			this.rangeNumber = 10;
41		},
42		// 获取选项
43		async getOptions() {
44			await searchCourseware().then(res => {
45				let list = res.data || [];
46				// 默认展示的数据
47				this.options = list;
48				// 原始数据
49				this.courseList = list;
50			});
51		}
52	}
53};
注意事项:
  • 定时器作用是防止输入筛选的关键字太过频繁,从而造成数据响应不及时;因为本次是一次性获取了全部的数据,所以这里只是用做渲染加载数据;
  • 选择器的事件函数主要是用来初始化“获取焦点”和“失去焦点”时处理默认展示数据用的,若是获取的网络请求,此处需要做“函数截流”处理。目的是减少接口请求次数。

「🍎」总结:

  换了新的工作环境,现开始着手做后台管理系统,或多或少会遇到各种各样的问题。一如即往,会在笔记中记录开发中遇到的问题。好记性不如烂笔头,希望现在埋下种子,等到来年秋天的时候会收获 🍒 果实。⛽️「加油 😯」