纯前端解决Antd select框渲染大量数据卡顿问题

一、模拟场景

在使用ant design vue开发时候,可能会遇到Select框需要加载几百条甚至上千条数据的场景。antd vue的select组件针对渲染大量数据的情况,暂时没做优化,因此每次点击select框都会感到卡顿。由于上次有小伙伴提出,想要纯前端实现的方法,因此我更新了这篇文章,下面是实现的效果预览:
在这里插入图片描述

二、优化方案

  1. 优化方案:初次加载30条左右(这个数量你可以自己决定,别太多就行)。当用户滚动下拉列表时,去加载更多数据。
  2. 实现思路:主要是合理运用Select组件的popupScroll函数。为了节流控制,我们需要引入lodash库里面的debounce,达到防抖动的目的。

三、代码演示

下面是优化后的代码,有一些地方需要按实际修改,思路啥的都注释在代码里了

<template>
 <a-form layout="inline">
    <a-form-item label="业务">
      <a-select
        v-model="selectedVal"
        :loading="dataLoading"
        placeholder="输入搜索内容"
        style="width: 240px;"
        show-search
        option-filter-prop="children"
        @search="handleSearch"
        @popupScroll="handlePopupScroll"
        @select="handleSelect"
      >
        <a-select-option v-for="item in renderedOptions" :value="item" :key="item">
          {{ item }}
        </a-select-option>
      </a-select>
    </a-form-item>
  </a-form>
</template>
<script>
import debounce from 'lodash/debounce'
import { fetchBusinessList } from '@/api/public' // 根据实际修改
const LOAD_NUM = 30 // 加载条数--可自定义

export default {
  name: 'BaseForm',
  data () {
    return {
      selectedVal: '', // Select框选中的值
      oriDataList: [], // 原数据列表 -- 从接口获取
      dataLoading: false, // 原数据列表的加载状态 -- 接口的响应状态
      searchVal: '', // 搜索的内容
      filterDataList: [], // 过滤的数据列表 -- 从dataList中过滤出含搜索内容的数据
      renderedOptions: [] // 已渲染的下拉列表
    }
  },
  mounted () {
    this.getDataList()
  },
  methods: {
    // 获取数据源,并取数据源的前n条作为下拉列表的可选项
    getDataList () {
      this.dataLoading = true
      fetchBusinessList().then(res => {
        this.dataLoading = false
        this.oriDataList = res.result // 该接口返回的数据存放在res.result(根据实际自行修改)
        this.renderedOptions = this.oriDataList.slice(0, LOAD_NUM)
      })
    },
    // 文本框值变化时触发 -- 从数据源中过滤出含搜索内容的数据,并取过滤结果的前n条作为下拉列表的可选项
    handleSearch (val) {
      this.searchVal = val
      let filterList = []
      if (val) {
        filterList = (this.oriDataList).filter(item => item.indexOf(val) > -1)
      } else {
        filterList = this.oriDataList
      }
      this.filterDataList = filterList
      this.renderedOptions = filterList.length < LOAD_NUM ? filterList : filterList.slice(0, LOAD_NUM)
    },
    // 滚动时触发(防止抖动)
    handlePopupScroll: debounce(function () {
      if (this.searchVal === '') {
        this.loadMoreData(this.oriDataList)
      } else {
        this.loadMoreData(this.filterDataList)
      }
    }, 400),
    // 加载更多数据到select框
    loadMoreData (dataList) {
      const renderedLen = this.renderedOptions.length // 已渲染的下拉列表长度
      const totalLen = dataList.length // 当前数据源的长度
      let addList = []
      if (renderedLen < totalLen) {
        if (renderedLen + LOAD_NUM <= totalLen) {
          addList = dataList.slice(renderedLen, renderedLen + LOAD_NUM)
        } else {
          addList = dataList.slice(renderedLen, renderedLen + (totalLen % LOAD_NUM))
        }
        this.renderedOptions = (this.renderedOptions).concat(addList)
      }
    },
    // 被选中时调用,参数为选中项的 value (或 key) 值
    handleSelect (val) {
      if (this.searchVal) {
        const selectedArr = (this.oriDataList).filter(item => item === val) // 从数据源中过滤出下拉框选中的值,并返回一个数组
        const restList = (this.oriDataList).filter(item => item !== val) // 从数据源中过滤出其他的值,返回一个数组
        const newList = selectedArr.concat(restList).slice(0, LOAD_NUM) // 将选中的元素放到下拉列表的第一位
        this.renderedOptions = newList // 更新已渲染的下拉列表
        this.oriDataList = selectedArr.concat(restList) // 更新数据源
        this.searchVal = '' // 因为触发handleSelect函数时,会自动清空用户输入的内容。因此,searchVal需要重置。
      }
    }
  }
}
</script>
Logo

前往低代码交流专区

更多推荐