业务场景:服务端返回两万多条数据需要在下拉框中展示,直接渲染会导致页面卡顿且需要很长时间等待,用户体验极差,所以我们把这个el-select优化一下,直接上代码。
相关知识传送门:
<template> <div class="content"> <el-select v-model="chooseValue" filterable v-el-select-loadmore="loadMore(rangeNumber)"> <el-option v-for="(item, index) in options.slice(0, rangeNumber)" :key="index" :label="item.label" :value="item.value"> </el-option> </el-select> </div> </template> <script> export default { data() { return { chooseValue: "", options: [], rangeNumber: 10, }; }, methods: { getList() { // 测试数据25000条数据, 这里数据多少条无所谓,options.slice(0, rangeNumber)方法只会默认加载初始的10条数据 for (let i = 0; i < 25000; i++) { this.options.push({label: "选择"+i,value:"选择"+i}); } }, loadMore(n) { // n是默认初始展示的条数会在渲染的时候就可以获取,具体可以打log查看 // elementui下拉超过7条才会出滚动条,如果初始不出滚动条无法触发loadMore方法 return () => (this.rangeNumber += 5); // 每次滚动到底部可以新增条数 可自定义 }, }, beforeMount() { this.getList(); }, directives:{ 'el-select-loadmore':(el, binding) => { // 获取element-ui定义好的scroll盒子 const SELECTWRAP_DOM = el.querySelector(".el-select-dropdown .el-select-dropdown__wrap"); if(SELECTWRAP_DOM){ SELECTWRAP_DOM.addEventListener("scroll", function () { /** * scrollHeight 获取元素内容高度(只读) * scrollTop 获取或者设置元素的偏移值, * 常用于:计算滚动条的位置, 当一个元素的容器没有产生垂直方向的滚动条, 那它的scrollTop的值默认为0. * clientHeight 读取元素的可见高度(只读) * 如果元素滚动到底, 下面等式返回true, 没有则返回false: * ele.scrollHeight - ele.scrollTop === ele.clientHeight; */ const condition = this.scrollHeight - this.scrollTop <= this.clientHeight; if (condition) binding.value(); }); } }, } }; </script>
最后, 两万多条数据用户不可能一直滚动下去找自己想要数据,记得设置 filterable 属性,实现搜索功能;
问题: 如果仅设置 element-ui 的 filterable 属性 ,那么搜索的范围只有懒加载已滚动出的数据,导致搜索不全、不准确。
为了解决这个问题,我们又继续使用了 filter-method 属性并结合 visible-change 事件,以及搜索输入时增加防抖进行优化。
最终代码:
// utils.js function _debounce(fn, delay = 300) { var timer = null; return function () { var _this = this; var args = arguments; if (timer) clearTimeout(timer); timer = setTimeout(function () { fn.apply(_this, args); }, delay); }; } export { _debounce }
<template> <div class="content"> <el-select v-model="chooseValue" clearable filterable :filter-method="filterMethod" v-el-select-loadmore="loadMore(rangeNumber)" @visible-change="visibleChange"> <el-option v-for="(item, index) in options.slice(0, rangeNumber)" :key="index" :label="item.label" :value="item.value"> </el-option> </el-select> </div> </template> <script> import {_debounce} from '@/utils/index.js' export default { data() { return { chooseValue: "", options: [], rangeNumber: 10, resOptions:[], }; }, methods: { // 模拟获取大量数据 getList() { // 测试数据15000条数据, 这里数据多少条无所谓,options.slice(0, rangeNumber)方法只会默认加载初始的10条数据 for (let i = 0; i < 25000; i++) { this.resOptions.push({label: "选择"+i,value:"选择"+i}); } }, loadMore(n) { // n是默认初始展示的条数会在渲染的时候就可以获取,具体可以打log查看 // elementui下拉超过7条才会出滚动条,如果初始不出滚动条无法触发loadMore方法 return () => (this.rangeNumber += 5); // 每次滚动到底部可以新增条数 可自定义 }, // 筛选方法 filterMethod:_debounce(function(filterVal){ if(filterVal){ let filterArr = this.resOptions.filter((item)=>{ return item.label.toLowerCase().includes(filterVal.toLowerCase()) }) this.options = filterArr; }else{ this.options = this.resOptions; } },500), // 下拉框出现时,调用过滤方法 visibleChange(flag){ if(flag){ this.filterMethod() } }, }, beforeMount() { this.getList(); }, directives:{ 'el-select-loadmore':(el, binding) => { // 获取element-ui定义好的scroll盒子 const SELECTWRAP_DOM = el.querySelector(".el-select-dropdown .el-select-dropdown__wrap"); if(SELECTWRAP_DOM){ SELECTWRAP_DOM.addEventListener("scroll", function () { /** * scrollHeight 获取元素内容高度(只读) * scrollTop 获取或者设置元素的偏移值, * 常用于:计算滚动条的位置, 当一个元素的容器没有产生垂直方向的滚动条, 那它的scrollTop的值默认为0. * clientHeight 读取元素的可见高度(只读) * 如果元素滚动到底, 下面等式返回true, 没有则返回false: * ele.scrollHeight - ele.scrollTop === ele.clientHeight; */ const condition = this.scrollHeight - this.scrollTop <= this.clientHeight; if (condition) binding.value(); }); } }, } }; </script>
效果: