您好,登錄后才能下訂單哦!
本篇內容介紹了“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 屬性
<template> <el-select v-model="value" v-bind="$atts" v-on="$listeners" > <virtual-scroll-list ref="virtualListRef" ></virtual-scroll-list> </el-select> </template> <script setup> import VirtualScrollList from 'vue-virtual-scroll-list' const props = defineProps({ // 當前 value: { type: [String, Number], default: '', }, // 下拉展示的 options options: { type: Array, default: () => [], }, // label 鍵值 labelKey: { type: String, default: 'label', }, // value 鍵值 valueKey: { type: String, default: 'value', }, }) const { value, options, labelKey, valueKey } = toRefs(props) const virtualListRef = ref(null) </script>
根據官網文檔描述,有三個必填屬性,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
<template> <el-option :key="value" :label="label" :value="value" v-bind="$atts" v-on="$listeners" /> </template> <script setup> import { computed, toRefs } from 'vue' const props = defineProps({ source: { type: Object, default: () => {}, }, valueKey: { type: [String, Number], default: '', }, labelKey: { type: String, default: '', }, }) const { source, valueKey, labelKey } = toRefs(props) const value = computed(() => source.value[valueKey.value]) const label = computed(() => source.value[labelKey.value]) </script>
接著在父組件中引入封裝的 el-option 組件,這里我們取名為 OptionNode,然后傳入 data-key
, data-sources
, data-component
三個必填屬性,同時將 labelKey 和 valueKey 通過 extra-props
屬性傳遞給子組件。vue-virtual-scroll-list 組件需要顯式設置列表的高度和滾動條,不然在元素過多時會出現列表過長的情況,同時為了配合項目,這里我將 keeps
屬性設置為 20,也就是只渲染 20 個真實 DOM 節點
<template> <fe-select v-model="value" v-bind="$atts" v-on="$listeners" > <virtual-scroll-list ref="virtualListRef" + class="virtual-scroll-list" + :data-key="dataKey" + :data-sources="allOptions" + :data-component="OptionNode" + :keeps="20" + :extra-props="{ + labelKey, + valueKey, + }" ></virtual-scroll-list> </fe-select> </template> <script setup> import { toRefs, ref, nextTick, computed, watch, onMounted } from 'vue' import VirtualScrollList from 'vue-virtual-scroll-list' + import OptionNode from './option-node.vue' const props = defineProps({ value: { type: [String, Number], default: '', }, options: { type: Array, default: () => [], }, labelKey: { type: String, default: 'label', }, valueKey: { type: String, default: 'value', }, }) const { value, options, labelKey, valueKey } = toRefs(props) const virtualListRef = ref(null) + const dataKey = ref(valueKey) + const allOptions = options.value </script> + <style lang="scss" scoped> + .virtual-scroll-list { + height: 200px; + overflow: auto; + } + </style>
這個時候就可以初步實現虛擬列表滾動的效果了,但由于虛擬滾動僅渲染部分 DOM,所有還有兩個問題需要考慮
保存了選擇項后,再二次加載時,需要顯示保存項,這時需要從完整 options 中找到保存項并放在列表最上面
篩選列表選項時,需要從完整 options 找到符合要求的選項并加載,同時在關閉列表時,需要重置列表
針對以上兩個問題,我們引入一個 currentOptions 記錄當前的 options,通過 remote-method
實現搜索效果,通過 visible-change
事件實現重置列表。經過優化后的代碼如下
<template> <fe-select v-model="value" + filterable + remote + :remote-method="handleRemoteMethod" v-bind="$atts" + @visible-change="handleVisiableChange" v-on="$listeners" > <virtual-scroll-list ref="virtualListRef" class="virtual-scroll-list" :data-key="dataKey" :data-sources="currentOptions" :data-component="OptionNode" :keeps="20" :extra-props="{ labelKey, valueKey, }" ></virtual-scroll-list> </fe-select> </template> <script setup> import { toRefs, ref, nextTick, computed, watch, onMounted } from 'vue' import VirtualScrollList from 'vue-virtual-scroll-list' import OptionNode from './option-node.vue' + import { cloneDeep, isNil } from 'lodash-es' const props = defineProps({ value: { type: [String, Number], default: '', }, options: { type: Array, default: () => [], }, labelKey: { type: String, default: 'label', }, valueKey: { type: String, default: 'value', }, }) const { value, options, labelKey, valueKey } = toRefs(props) const virtualListRef = ref(null) const dataKey = ref(valueKey) + // 當前篩選的 options + const currentOptions = ref([]) + // 全量 options + // 注意這里需要深拷貝 + const allOptions = computed(() => cloneDeep(options.value)) + onMounted(() => { + handleInitOptions(allOptions.value, value.value) + }) + watch([value, options], ([newVal, newOptions], [_oldVal, oldOptions]) => { + // 異步加載 options 時,如果 value 有值,需要將 value 對應的 option 放在第一位 + if ((!isNil(newVal) || newVal !== '') && newOptions.length > 0 && oldOptions.length === 0) { + handleInitOptions(newOptions, newVal) + } + }) + /** + * @description 因為 DOM 不是全量加載,所以需要手動處理 + */ + function handleRemoteMethod(query) { + if (query !== '') { + currentOptions.value = allOptions.value.filter((item) => { + return item[labelKey.value].includes(query) + }) + } else { + currentOptions.value = allOptions.value + } + } + function handleVisiableChange(val) { + // 隱藏下拉框時,重置數據 + if (!val) { + virtualListRef.value && virtualListRef.value.reset() + nextTick(() => { + currentOptions.value = allOptions.value + }) + } + } + /** + * @description 異步加載 options 時,如果 value 有值,需要將 value 對應的 option 放在第一位 + */ + function handleInitOptions(allOptions, value) { + const existOption = allOptions.find((item) => { + return item[valueKey.value] === value + }) + if (existOption) { + currentOptions.value.push(existOption) + } + currentOptions.value.push( + ...allOptions.filter((item) => { + return item[valueKey.value] !== value + }) + ) + } </script> <style lang="scss" scoped> .virtual-scroll-list { height: 200px; overflow: auto; } </style>
“element select怎么實現組件虛擬滾動優化”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。