淺談ElementUI el-select 數據過多解決辦法

1. 場景描述

不知道你有沒有這樣的經歷,下拉框的選項很多,上萬個選項甚至更多,這個時候如果全部把數據放到下拉框中渲染出來,瀏覽器會卡死,體驗會特別不好

用人會說element-ui的select有一個remote-method,支持遠程搜索,我們讓服務端支持一下不就可以瞭,當然這是一種解決的方案。但是有時候這種方法有時候不一定適用

(1)有時候服務端數據是經過計算返回給我們的,可能返回不是特別快,體驗不是很好
(2)有時候數據可能隻有幾千條,全部渲染又不太合適,一直掉接口不是特別好
(3)僅僅通過前端能不能解決,如果能解決,豈不是減輕瞭服務端的工作和壓力

2.解決辦法

1 ) 分段加載:也不加載下拉項,通過點擊下拉框的時候,再去加載,此時的選項全部加載進來,該種情況隻適用於緩加載情況,需要點擊加載完後才能下拉選項,體驗一般。
2 )提示:element-ui的select有一個filter-method方法,我們可以通過這個方法來進行過濾下拉項
假設我們有個下拉框是用來選擇用戶的

<el-select
  v-model="userId"
  filterable
  :filter-method="userFilter"
  clearable>
  <el-option
    v-for="item in userList"
    :key="item.userId"
    :label="item.username"
    :value="item.userId"
  ></el-option>
</el-select>
userFilter(query = '') {
  let arr = this.allUserList.filter((item) => {
    return item.username.includes(query) || item.userId.includes(query)
  })
  if (arr.length > 50) {
    this.userList = arr.slice(0, 50)
  } else {
    this.userList = arr
  }
},
getUserWhiteList() {
  HttpRequest.post("/api/admin/community/getUserWhiteList").then(
    response => {
      this.allUserList = response.data.list;
      this.userFilter()
    }
  );
},

如上所示,我們從後臺獲取用戶列表,經過我們自己的過濾,我們每次隻渲染50條數據,無論有多少數據,對我們來說也支持一個變量,占個內存。當然數據越多,數組的遍歷也會相應的慢,但是這個影響不大。
我們不僅能過濾名字,還可以對我們制定的任一項進行過濾
優化:上面的代碼我們還可以適當優化下,隻有發現瞭數組長度超過瞭50項,我們就停止遍歷

 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頭部

到此這篇關於ElementUI el-select 數據過多解決辦法的文章就介紹到這瞭,更多相關ElementUI el-select 數據過多內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: