element select怎么实现组件虚拟滚动优化
不知道大家在开发过程中有没有遇到这样一个场景,后端接口一次性返回上千条数据(比如国家地区),接口不支持分页,不能筛选,只能前端自己通过 select 组件全量渲染出来。这种渲染大量 DOM 的场景下会造成页面非常卡顿,我在网上搜索了一下一般有两种解决方案
前端自己实现数据分页效果
虚拟滚动,比如 element plus 就有专门的 select 虚拟滚动组件
最好的方案当然时直接使用成熟的轮子,奈何我们的项目是 vue 2.7,所以只能借助支持 vue2 的虚拟滚动组件 vue-virtual-scroll-list,自己封装一个 select 虚拟滚动组件
组件实现
首先在项目中引入 vue-virtual-scroll-list
npm i vue-virtual-scroll-list
接下来开发封装虚拟滚动组件,因为使用的是 vue2.7 版本,为了以后项目升级 vue3,所以直接使用 composition api 的方式开发。在 el-select 组件内部引入安装好的 vue-virtual-scroll-list,定义好组件的基础结构,和需要传入的 props 属性
根据官网文档描述,有三个必填属性,data-key, data-sources, data-component,我们可以直接选取 value 作为唯一的 data-key,data-sources 就是我们传入的 options,data-component 需要我们将 el-option 封装为一个独立的组件
| 属性 | 是否必填 | 默认值 | 类型 | 描述 |
|---|---|---|---|---|
data-key | 必填 | String | Function | 虚拟滚动列表每一项的唯一 id,如果是函数的话需要返回 string | |
data-sources | 必填 | Array[Object] | 虚拟滚动的数据列表,每个数组项必须是对象,并且每个对象必须有一个唯一的属性与 data-key 匹配 | |
data-component | 必填 | Component | 虚拟滚动每一项的渲染组件 | |
keeps | 非必填 | 30 | Number | 虚拟列表展示的真实 DOM 的数量 |
extra-props | 非必填 | {} | Object | 传递给子组件的额外参数 |
我们先将 el-option 封装为一个独立组件,需要注意的是 vue-virtual-scroll-list 默认传入是数组是 source,所以需要从 source 属性中根据 labelKey 和 valueKey 找到需要加载的 label 和 value
接着在父组件中引入封装的 el-option 组件,这里我们取名为 OptionNode,然后传入 data-key, data-sources, data-component 三个必填属性,同时将 labelKey 和 valueKey 通过 extra-props 属性传递给子组件。vue-virtual-scroll-list 组件需要显式设置列表的高度和滚动条,不然在元素过多时会出现列表过长的情况,同时为了配合项目,这里我将 keeps 属性设置为 20,也就是只渲染 20 个真实 DOM 节点
+
这个时候就可以初步实现虚拟列表滚动的效果了,但由于虚拟滚动仅渲染部分 DOM,所有还有两个问题需要考虑
保存了选择项后,再二次加载时,需要显示保存项,这时需要从完整 options 中找到保存项并放在列表最上面
筛选列表选项时,需要从完整 options 找到符合要求的选项并加载,同时在关闭列表时,需要重置列表
针对以上两个问题,我们引入一个 currentOptions 记录当前的 options,通过 remote-method 实现搜索效果,通过 visible-change 事件实现重置列表。经过优化后的代码如下