vue插件 vue-virtual-scroll-list解决数据量太大问题

项目中数据量有时候过于庞大,使用elementui的Select组件时,会导致下拉框加载速度慢卡顿甚至于卡死,为解决这个问题,使用vue-virtual-scroll-list插件 ,模拟虚拟滚动。

vue-virtual-scroll-list是vue的一个虚拟滚动组件,通过不渲染可视区域以外的内容,显示虚拟的滚动条来提升页面性能

首先看

安装

npm install vue-virtual-scroll-list --save

参数及方法

常用参数

PropTypeDescription
data-key String|Function 从data-sources每个数据对象中获取唯一键。或每个函数都调用data-source并返回其唯一键。在中,其值必须唯一data-sources,用于标识商品尺寸。
data-sources Array[Object] 列表数据,每一行都必须有一个唯一的id(data-key)
data-component Component 每一行的子组件
keeps Number 默认30个,默认渲染的个数
extra-props Object 默认{} data-component组件的额外props通过改属性传入,内部已有source和index两个

常用方法

可以通过以下方式调用这些方法ref:

MethodDescription
reset() 将所有状态重置为初始状态。
scrollToIndex(index) 手动设置滚动位置到指定索引。

上述是组件用到的方法,其他参数及方法可查看官网

引入使用

1 import virtualList from 'vue-virtual-scroll-list'
2 components:{
3     'virtual-list': virtualList
4 },

基础用法

  • 属性说明

data-key=“selectData.value” 就是绑定的唯一key值,可传入动态的

data-sources=“selectArr” 下拉框的数组

data-component=“itemComponent” 就是抽离中的el-option组件

keeps=“20” 渲染的个数

extra-props 值为对象,可以传入自定义属性进去

  • 高度设置
 1 .virtualselect {
 2     // 设置最大高度
 3     &-list {
 4         max-height:245px;
 5         overflow-y:auto;
 6     }
 7     .el-scrollbar .el-scrollbar__bar.is-vertical {
 8         width: 0;
 9     }
10 }

原理

 

只渲染keeps传入的个数,滚动时通过改变padding的值来模拟滚动,里面的每一个item在滚动时动态替换里面的值,永远只渲染keeps传入的个数,所以这样不会卡

  • 回显问题

由于只渲染20条数据,当默认数据处于20条之外,在回显的时候会显示异常
解决方法: 遍历所有数据,将对应回显的那一条数据放在第一条即可

 1 this.selectArr = JSON.parse(JSON.stringify(this.selectData.data));
 2 let obj = {};
 3 for (let i = 0; i < this.selectArr.length; i++) {
 4     const element = this.selectArr[i];
 5     if(element[this.selectData.value].toLowerCase() === this.defaultValue.toLowerCase()) {
 6         obj = element;
 7         this.selectArr.splice(i,1);
 8         break;
 9     }
10 }
11 this.selectArr.unshift(obj);
  • 模糊搜索

使用select自带的filterable,filter-method自定义属性实现过滤数据

 1 <el-select filterable :filter-method="filterMethod" @visible-change="visibleChange" >
 2 </el-select>
 3 
 4 // 搜索
 5 filterMethod(query) {
 6     if (query !== '') {
 7         this.$refs.virtualList.scrollToIndex(0);//滚动到顶部
 8         setTimeout(() => {
 9             this.selectArr = this.selectData.data.filter(item => {
10                 return this.selectData.isRight?
11                     (item[this.selectData.label].toLowerCase().indexOf(query.toLowerCase()) > -1 || item[this.selectData.value].toLowerCase().indexOf(query.toLowerCase()) > -1)
12                     :item[this.selectData.label].toLowerCase().indexOf(query.toLowerCase()) > -1;
13             });
14         },100)
15     } else {
16         this.init();
17     }
18 },
19 visibleChange(bool) {
20     if(!bool) {
21         this.$refs.virtualList.reset();
22         this.init();
23     }
24 }

完整代码

  • Select.vue
      1 <template>
      2     <div>
      3         <el-select :value="defaultValue" popper-class="virtualselect" filterable :filter-method="filterMethod" @visible-change="visibleChange" v-bind="$attrs" v-on="$listeners">
      4             <virtual-list ref="virtualList" class="virtualselect-list"
      5                 :data-key="selectData.value"
      6                 :data-sources="selectArr"
      7                 :data-component="itemComponent"
      8                 :keeps="20"
      9                 :extra-props="{
     10                     label: selectData.label,
     11                     value: selectData.value,
     12                     isRight: selectData.isRight
     13                 }"></virtual-list>
     14         </el-select>
     15     </div>
     16 </template>
     17 <script>
     18 import virtualList from 'vue-virtual-scroll-list'
     19 import ElOptionNode from './el-option-node'
     20 export default {
     21     components:{
     22         'virtual-list': virtualList
     23     },
     24     model: {
     25         prop: 'defaultValue',
     26         event: 'change',
     27     },
     28     props: {
     29         selectData: {
     30             type: Object,
     31             default () {
     32                 return {}
     33             }
     34         },//父组件传的值
     35         defaultValue: {
     36             type: String,
     37             default: ''
     38         }// 绑定的默认值
     39     },
     40     mounted() {
     41         this.init();
     42     },
     43     watch: {
     44         'selectData.data'() {
     45             this.init();
     46         }
     47     },
     48     data() {
     49         return {
     50             itemComponent: ElOptionNode,
     51             selectArr:[]
     52         }
     53     },
     54     methods: {
     55         init() {
     56             if(!this.defaultValue) {
     57                 this.selectArr = this.selectData.data;
     58             }else {
     59                 // 回显问题
     60                 // 由于只渲染20条数据,当默认数据处于20条之外,在回显的时候会显示异常
     61                 // 解决方法:遍历所有数据,将对应回显的那一条数据放在第一条即可
     62                 this.selectArr = JSON.parse(JSON.stringify(this.selectData.data));
     63                 let obj = {};
     64                 for (let i = 0; i < this.selectArr.length; i++) {
     65                     const element = this.selectArr[i];
     66                     if(element[this.selectData.value].toLowerCase() === this.defaultValue.toLowerCase()) {
     67                         obj = element;
     68                         this.selectArr.splice(i,1);
     69                         break;
     70                     }
     71                 }
     72                 this.selectArr.unshift(obj);
     73             }
     74         },
     75         // 搜索
     76         filterMethod(query) {
     77             if (query !== '') {
     78                 this.$refs.virtualList.scrollToIndex(0);//滚动到顶部
     79                 setTimeout(() => {
     80                     this.selectArr = this.selectData.data.filter(item => {
     81                         return this.selectData.isRight?
     82                             (item[this.selectData.label].toLowerCase().indexOf(query.toLowerCase()) > -1 || item[this.selectData.value].toLowerCase().indexOf(query.toLowerCase()) > -1)
     83                             :item[this.selectData.label].toLowerCase().indexOf(query.toLowerCase()) > -1;
     84                     });
     85                 },100)
     86         } else {
     87             this.init();
     88         }
     89         },
     90         visibleChange(bool) {
     91             if(!bool) {
     92                 this.$refs.virtualList.reset();
     93                 this.init();
     94             }
     95         }
     96     }
     97 }
     98 </script>
     99 <style lang="scss" scoped>
    100     .virtualselect {
    101         // 设置最大高度
    102         &-list {
    103             max-height:245px;
    104             overflow-y:auto;
    105         }
    106         .el-scrollbar .el-scrollbar__bar.is-vertical {
    107             width: 0;
    108         }
    109     }
    110     
    111 </style>

     

  • el-option-node.vue

     1 <template>
     2     <el-option :key="label+value" :label="source[label]" :value="source[value]">
     3         <span>{{source[label]}}</span>
     4         <span v-if="isRight" style="float:right;color:#939393">{{source[value]}}</span>
     5     </el-option>
     6 </template>
     7 <script>
     8 export default {
     9     name: 'item-component',
    10     props: {
    11         index: {
    12             type: Number
    13         },// 每一行的索引
    14         source: {
    15             type: Object,
    16             default () {
    17                 return {}
    18             }
    19         },// 每一行的内容
    20         label: {
    21             type: String
    22         },// 需要显示的名称
    23         value: {
    24             type: String
    25         },// 绑定的值
    26         isRight: {
    27             type: Boolean,
    28             default () {
    29                 return false
    30             }
    31         }// 右侧是否显示绑定的值
    32     }
    33 }
    34 </script>

     

  • parent.vue

  

 1 <template>
 2     <cw-select :selectData="selectData" v-model="defaultValue" placeholder="请选择下拉数据" clearable @change="selectChange"></cw-select>
 3 </template>
 4 
 5 <script>
 6     import CwSelect from '@/components/Select'
 7     export default {
 8         components: {
 9             CwSelect
10         },
11         data() {
12             return {
13                 selectData: {
14                     data:[],// 下拉框数据
15                     label: 'name',// 下拉框需要显示的名称
16                     value: 'code',// 下拉框绑定的值
17                     isRight: true,//右侧是否显示
18                 },
19                 defaultValue: 'China99', //下拉框选择的默认值
20             };
21         },
22         mounted() {
23             this.selectData.data = []
24             for (let i = 0; i < 10000; i++) {
25                 this.selectData.data.push({code:'China'+i,name:'中国'+i+''})
26             }
27         },
28         methods: {
29             selectChange(val) {
30                 console.log('下拉框选择的值', val)
31             }
32         }
33     };
34 </script>

 

posted @ 2024-04-11 18:26  yuwenjing  阅读(2743)  评论(0编辑  收藏  举报