el-select組件的options條數過多時的解決方案

業務場景


當使用el-select組件時,如果options數量過多,會存在的弊端:

  • 頁面渲染出大量el-option節點,會導致頁面卡頓甚至卡死,用戶體驗極差。
  • 選擇時條目衆多,查找困難。

本次我遇到的場景是options數量爲6-9千的情況。

解決思路


從總options中取出固定條目的小option(renderOption)用於頁面渲染,利用el-select提供的
filter-method方法進行搜索過濾,在搜索時用過濾結果更新renderOption。

代碼實現


下面是vue的組件封裝

<template>
    <el-select
        class="yt-select"
        v-model="currValue"
        filterable
        v-bind="$attrs"
        :filter-method="userFilter"
        :disabled="disabled"
        :clearable="clearable"
        @change="change"
    >
        <el-option
            v-for="option in renderOption"
            :key="option.value"
            :value="option.value"
            :label="option.label"
        >{{ option.label }}</el-option>
    </el-select>
</template>

<script>

export default {
    name: 'easy-select',
    props: {
        value: {
            type: [String, Number],
            default: ''
        },
        max: {
            type: Number,
            default: 30
        },
        disabled: {
            type: Boolean,
            default: false
        },
        clearable: {
            type: Boolean,
            default: true
        },
        options: {
            type: Array,
            default: () => []
        }
    },
    data () {
        return {
            renderOption: []
        }
    },
    computed: {
        currValue: {
            get () {
                return this.value || ''
            },
            set (value) {
                this.$emit('input', value)
            }
        }
    },
    watch: {
        value () {
            this.addValueOptions()
        },
        options: {
            handler (V) {
                this.init()
            },
            deep: true
        }
    },
    created () {
        this.init()
    },
    methods: {
        async init () {
            this.userFilter()
            this.addValueOptions()
        },
        addValueOptions () {
            if (this.currValue) {
                let target = this.options.find((item) => { // 從大option中找到當前條
                    return item.value === this.currValue
                })
                if (target) { // 將當前條與小option比對,沒有則加入
                    if (this.renderOption.every(item => item.value !== target.value)) {
                        this.renderOption.unshift(target)
                    }
                }
            }
        },
        addFilterOptions (label) {
        	// 每次查找輸入時,若有精確匹配的條目,保證該條目一定在renderOption內
            let target = this.options.find((item) => { // 從大option中找到當前條
                return item.label === label
            })
            if (target) { // 將當前條與小option比對,沒有則加入
                if (this.renderOption.every(item => item.label !== target.label)) {
                    this.renderOption.unshift(target)
                }
            }
        },
        userFilter (query = '') {
            let arr = this.options.filter((item) => {
                return item.label.includes(query) || item.value.includes(query)
            })
            if (arr.length > this.max) {
                this.renderOption = arr.slice(0, this.max)
                this.addFilterOptions(query)
            } else {
                this.renderOption = arr
            }
        },
        change (value) {
            this.$emit('change', value)
            if (!value) { // 單選清空-optons初始化下
                this.userFilter()
            }
        }
    }
}
</script>

注意事項


  • 初始化和value值變化時,需要找到value對應具體項,並加入renderOptions
  • 搜索時,可能過濾到的n條數據都沒有包含用戶想找的具體項,因此,過濾時需要進行一下精確查找,將匹配項放入renderOptions頭部

如果您有什麼好的思路與建議,歡迎在評論區討論~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章