el-select应用虚拟列表,避免过多数据导致浏览器卡死

el-select:

        element-ui组件中的select下拉选择组件,支持单选、多选等

虚拟列表:

        虚拟列表是一种优化技术,用于处理大型列表。在传统的列表中,当用户滚动到底部时,列表会加载所有的数据,这可能导致性能问题和内存泄漏。虚拟列表通过只加载当前可见的部分数据,而不是整个列表的数据来解决这个问题。当用户滚动到新的区域时,虚拟列表会自动加载新的数据,从而提高性能和响应速度。这种技术通常与虚拟滚动结合使用,可以在保持高性能和流畅性的同时,支持大量的数据呈现。

背景:

        在做管理系统,下拉选择框是个必备功能,在实际开发中,下拉数据量是不可控的,el-select虽然有过滤筛选等功能,但是如果数据过大的时候,会导致浏览器卡死,在切换页面时,销毁这个也很耗时,导致页面切换体验性很差,所以需要处理这种情况!

最简单处理方案:

        默认下拉是空,只支持过滤筛选,实现:filterMethod,可以手动控制展示的数据量

最终处理方案:

        用虚拟列表策略处理,重新封装el-select组件

<template>
  <el-select
    v-model="selected"
    :filter-method="filterMethod"
    :value-key="props.key ? props.key : props.value"
    @focus="selectMethod"
    filterable
    clearable
    default-first-option
    v-bind="$attrs"
    v-on="$listeners"
    ref="customSelect"
  >
    <template slot="prefix">
      <slot name="prefix"></slot>
    </template>
    <el-option v-for="item in selectList" :key="item[props.key ? props.key : props.value]" :value="item[props.value]" :label="item[props.label]"></el-option>
  </el-select>
</template>

<script>
import { throttle } from '@/utils/debounce';
export default {
  props: {
    value: [String, Number, Array],
    props: {
      type: Object,
      default: () => {
        return {
          value: 'id',
          label: 'name',
        };
      },
    },
    data: {
      type: Array,
      default: () => [],
    },
  },
  computed: {
    selected: {
      get() {
        return this.value;
      },
      set(val) {
        this.$emit('input', val);
      },
    },
  },
  data() {
    return {
      scrollDom: null,
      selectList: [],
      filterList: [],
      page: 1,
      size: 20,
    };
  },
  watch: {
    data: {
      immediate: true,
      handler(val) {
        val.length && this.initSelect();
      },
    },
  },
  mounted() {
    this.scrollDom = this.$refs.customSelect.$refs.scrollbar.$refs.wrap;
    this.scrollDom.addEventListener('scroll', throttle(this.handlerScroll, 200));
  },
  beforeDestroy() {
    this.scrollDom.removeEventListener('scroll', throttle(this.handlerScroll, 200));
  },
  methods: {
    handlerScroll() {
      // TODO:监听滚动,触发加载
      if (this.scrollDom.scrollHeight - this.scrollDom.scrollTop - 1 <= this.scrollDom.clientHeight) {
        this.loadData();
      }
    },
    firstLoad() {
      // 初始化数据
      this.page = 1;
      const len = this.filterList.length;
      if (len <= this.size) {
        this.selectList = this.filterList;
      } else {
        this.selectList = this.filterList.slice(0, this.size);
      }
    },
    selectMethod() {
      let param = this.selected;
      if (Object.prototype.toString.call(this.selected) === '[object Array]') {
        param = this.selected.join('');
      }
      if (param) {
        // TODO:有值不需要重置查询, 但当组件初始化的时候selected有值回显有问题
      } else {
        this.filterMethod('');
      }
    },
    filterMethod(str) {
      this.filterList = this.data.filter(item => {
        return item[this.props.label] && item[this.props.label].indexOf(str) > -1;
      });
      this.firstLoad();
    },
    initSelect() {
      let param = this.selected;
      if (Object.prototype.toString.call(this.selected) === '[object Array]') {
        param = this.selected.join('');
      }
      if (param) {
        // 筛选
        this.filterList = this.data.filter(item => {
          return param.indexOf(item[this.props.value]) > -1;
        });
        this.selectList = this.filterList;
      }
    },
    loadData() {
      if (this.filterList.length <= this.page * this.size) {
        // 最后一页
        return;
      }
      ++this.page;
      const len = this.page * this.size;
      if (this.filterList.length <= len) {
        this.selectList = this.filterList;
      } else {
        this.selectList = this.filterList.slice(0, len);
      }
    },
  },
};
</script>

猜你喜欢

转载自blog.csdn.net/chen548246/article/details/133685905
今日推荐