通过 vue-virtual-scroll-list 库对 element UI 的 Select 组件进行性能优化

2023-03-08 上午前端 143 次浏览2条评论

前言

今天在开发过程中发现,当下列菜单的项过多时(大于10000条),页面会有明显的卡顿。这里记录一下通过 vue-virtual-scroll-list 对下拉菜单进行性能优化。

项目中使用的是 element ui 组件库,下面是基于element ui进行封装的,其他组件库的封装都大同小异,这里就不赘述了。

这里实现了长列表的优化,也实现了长列表的搜索功能,毕竟如果是几万条数据,不通过搜索根本无法查找数据。

代码实现

// VirtualSelect.vue 
<template>
    <div class="virtual-select">
        <el-select
           v-modle="myValue"
           :placeholder="placeholder"
           filterable
           clearable
           remote
           :remote-method="getFilterData"
           @visible-change="visibleOption">
            <virtual-list 
              ref="virtualList"
              class="virtual-list"
              :data-key="primaryKey"
              :data-sources="filterData"
              :data-component="componentItem"
            />
        </el-select>
    </div>
</template>
<script>
import VirtualList from 'vue-virtual-scroll-list'
export default {
    components: {
        VirtualList
    },
    props: {
        value: {
            default: () => {}
        },
        dataList: {
            type: Array,
            default: () => []
        },
        primaryKey: {
            type: String,
            default: ''
        },
        searchKey: {
            type: Array,
            default: () => []
        },
        placeholder: {
            type: String,
            default: ''
        },
        componentItem: {
            default: ''
        }
    },
    data() {
        return {
            myValue: '',
            data: [],
            filterData: []
        }
    },
    watch: {
        dataList(val) {
            if (val) {
                this.data = val
                this.filterData = val
            }
        },
        value(val) {
            this.myValue = val
        },
        myValue(val) {
            this.$emit('input', val)
        }
    },
    methods: {
        getFilterData(query) {
            if (query !== '') {
                this.$refs.virtualList.reset()
                this.filterData = this.data.filter(item => {
                    if(this.searchKey.length > 0) {
                        const res = this.searchKey.filter(searchVal => {
                            return item[searchVal].toString().toLowerCase().indexOf(query.toLowerCase()) !== -1
                        })
                        return res.length > 0
                    }
                })
            } else {
                this.filterData = this.data
            }
        },
        visibleOption(bool) {
            if (!bool) {
                setTimeout(() => { this.filterData = this.data }, 10)
            } else {
                this.$refs.virtualList.reset()
            }
        }
    }
}
</script>
<style lang="scss">
    .virtual-list {
        max-height: 235px;
        overflow-y: auto;
        &::-webkit-scrollbar {
            width: 10px;
        }
        &::-webkit-scrollbar-track {
            background: #fff;
            border-radius: 10px;
        }
        &::-webkit-scrollbar-thumb {
            background: #e7e7e7;
            border-radius: 10px;
        }
    }
    .virtual-select {
        .el-select {
            .el-select__caret:first-child::before {
                content: '\e6e1'
            }
            .if-focus {
                .el-select__caret:first-child {
                    transform: rotate(0deg);
                }
            }
        }
    }
</style>
// VirtualOption.vue
<template>
    <el-option
         :label="source.name+'-'+source.phone"
         :value="source.id"
    />
</template>
<script>
export default {
    props: {
        source: {
            type: Object,
            default: () => {}
        }
    }
}
</script>

使用:

<template>
    <virtual-select
        v-model="currentData"
        :dataList="dataList"
        primaryKey="id"
        :searchKey="['name', 'phone']"
        placeholder="请选择数据"
        :componentItem="virtualOption"
    />
</template>
<script>
import VirtualSelect from './components/VirtualSelect.vue'
import VirtualOption from './components/VirtualOption.vue'
export default {
    components: { VirtualSelect },
    data() {
        return {
            currentData: null,
            dataList: [
                { id: 1, name: '小杜', phone: 13833222233 },
                { id: 2, name: '小王', phone: 18322333322 }
            ],
            virtualOption: VirtualOption
        }
    }
}
</script>

目录

代码实现
ICP备案号:鲁ICP备2020040322号