中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

element?select怎么實現組件虛擬滾動優化

發布時間:2023-04-11 16:11:53 來源:億速云 閱讀:434 作者:iii 欄目:開發技術

本篇內容介紹了“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-keydata-sources 就是我們傳入的 options,data-component 需要我們將 el-option 封裝為一個獨立的組件

屬性是否必填默認值類型描述
data-key必填
String | Function虛擬滾動列表每一項的唯一 id,如果是函數的話需要返回 string
data-sources必填
Array[Object]虛擬滾動的數據列表,每個數組項必須是對象,并且每個對象必須有一個唯一的屬性與 data-key 匹配
data-component必填
Component虛擬滾動每一項的渲染組件
keeps非必填30Number虛擬列表展示的真實 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怎么實現組件虛擬滾動優化”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

溧水县| 泰州市| 新沂市| 孝感市| 苍溪县| 康保县| 常州市| 万山特区| 廊坊市| 枣庄市| 全椒县| 留坝县| 兴山县| 济南市| 武义县| 来安县| 陆丰市| 日土县| 沂水县| 天祝| 汉寿县| 饶平县| 三都| 临沧市| 新源县| 兴安县| 金平| 稻城县| 临洮县| 石城县| 荥经县| 昂仁县| 大庆市| 清新县| 黎平县| 民乐县| 克山县| 拜泉县| 寿光市| 兴和县| 东乌珠穆沁旗|