vue2虚拟滚动下拉选择器

vue虚拟滚动下拉选择器

目的

为了解决 element-ui 中 el-select 组件在大数据量的情况下出现的性能问题(数据量太大,导致渲染过慢,或造成页面卡顿甚至于卡死) 。

原理

本组件时基于vue-virtual-scroll-list(一个基于vue2的虚拟滚动组件,通过不渲染可视区域以外的内容,显示虚拟的滚动条来提升页面性能)实现的下拉选中框,该下拉选择组件支持单选,多选,筛选等操作,若是选中了数据,再次打开会自动定位到数据的位置

一.安装

npm install vue-virtual-scroll-list --save
常用参数及方法
常用参数
参数名类型描述
data-keyString|Function从每个数据对象中的“数据源”获取唯一键。或者用每个“数据源”调用一个函数,并返回它们的唯一键。它的值在“数据源”中必须是唯一的,用于标识条目的大小。
data-sourcesArray[Object]为列表构建的源数组,每个数组数据必须是一个对象,并具有唯一的键ID(data-key)属性。
data-componentComponentvue创建/声明的渲染项组件,它将使用’ data-sources ‘中的数据对象作为渲染道具,并命名为’source’。
keepsNumber你希望虚拟列表在真正的dom中保持呈现多少项,默认30个。
extra-props Object分配给不在数据源中的项目组件的额外道具。注意:索引和源都被占用在内部。
常用方法

可以通过ref方式调用以下这些方法:

方法名描述
reset将所有状态重置为初始状态。
scrollToIndex(index)手动设置滚动位置为指定的索引。
scrollToOffset(offset)手动设置滚动位置到指定的偏移量。

上述是组件常用的参数和方法,其他参数及方法可 官方插件

二.开始封装element的select组件

在src/components中创建文件夹SelectV2

1.创建index.vue文件
<template>
  <div>
    <el-select
      :value="value"
      popper-class="virtualselect"
      filterable
      :filter-method="filterMethod"
      @visible-change="visibleChange"
      v-bind="$attrs"
      v-on="$listeners"
    >
      <vue-virtual-list
        ref="virtualList"
        class="virtualselect-list"
        :data-key="fields.value"
        :data-sources="dataSources"
        :data-component="itemComponent"
        :keeps="20"
        :extra-props="{
          label: fields.label,
          value: fields.value,
          rightLabel: fields.rightLabel
        }"
      ></vue-virtual-list>
    </el-select>
  </div>
</template>

<script>
import VueVirtualList from 'vue-virtual-scroll-list';
import itemComponent from './itemComponent';

export default {
  name: 'select-v2',
  components: {
    'vue-virtual-list': VueVirtualList
  },
  model: {
    prop: 'value',
    event: 'change'
  },
  props: {
    // 下例列表数据
    data: {
      type: Array,
      default: () => []
    },
    // 字段参数配置,默认为label,value
    // 可尝试传入rightLabel字段,使其在右边也展示内容
    fields: {
      type: Object,
      default() {
        return {
          label: 'label',
          value: 'value',
        };
      }
    },
    // 绑定的默认值
    value: {
      type: [String, Array],
      default: () => []
    }
  },
  mounted() {
    this.init();
  },
  watch: {
    'data'() {
      this.init();
    }
  },
  data() {
    return {
      // 内容组件
      itemComponent,
      // 下拉列表中的数据
      dataSources: [],
      // 用来定位滚动到某个位置
      start: 0
    };
  },
  methods: {
    init() {
      if (!this.value || this.value.length === 0) {
        // 初始化数据
        this.dataSources = this.data;
      } else {
        // 回显问题
        // 单选时,存储当前的index
        // 多选时,取选中的所有值中下标的最小值
        this.dataSources = JSON.parse(JSON.stringify(this.data));
        if (typeof this.value === 'string') {
          const value = this.value
          for (let i = 0; i < this.dataSources.length; i++) {
            const element = this.dataSources[i];
            if (element[this.fields.value] === value) {
              this.start = i
              break;
            }
          }
        } else if (Array.isArray(this.value)) {
          let start = []
          const valueSet = new Set(this.value)
          for (let i = 0; i < this.dataSources.length; i++) {
            const element = this.dataSources[i];
            if (valueSet.has(element[this.fields.value])) {
              start.push(i)
            }
          }
          this.start = Math.min(...start)
        }
      }
    },
    // 搜索
    filterMethod(query) {
      if (query !== '') {
        setTimeout(() => {
          this.$refs.virtualList.scrollToIndex(0);
          this.dataSources = [...this.data.filter((item) => {
            return this.fields.rightLabel
                ? item[this.fields.label]
                    .indexOf(query) > -1 ||
                item[this.fields.rightLabel]
                    .indexOf(query) > -1
                : item[this.fields.label]
                .indexOf(query) > -1;
          })];
        }, 100);
      } else {
        this.init();
      }
    },
    visibleChange(bool) {
      if (!bool) {
        this.$refs.virtualList.reset();
        this.init();
      } else {
        // 定位到选中的位置
        this.$nextTick(() => {
          this.$refs.virtualList.scrollToIndex(this.start);
        })
      }
    }
  }
};
</script>
<style lang="scss">
.virtualselect {
  // 设置最大高度
  &-list {
    max-height: 245px;
    overflow-y: auto;
  }

  .el-scrollbar .el-scrollbar__bar.is-vertical {
    width: 0 !important;
  }
}
</style>
2.同目录下创建itemComponent.vue文件
<template>
  <div>
    <el-option
        :key="label + value"
        :label="source[label]"
        :value="source[value]"
    >
      <span>{{ source[label] }}</span>
      <span v-if="rightLabel" style="float:right;color:#939393">{{
          source[rightLabel]
        }}</span>
    </el-option>
  </div>
</template>

<script>
export default {
  name: 'item-component',
  props: {
    // index of current item
    // 每一行的索引
    index: {
      type: Number
    },
    // 每一行的内容
    source: {
      type: Object,
      default() {
        return {};
      }
    },
    // 需要显示的名称
    label: {
      type: String
    },
    // 绑定的值
    value: {
      type: String
    },
    // 右侧显示绑定的值,为空则不显示
    rightLabel: {
      type: String,
      default: ''
    }
  },
  mounted() {}
};
</script>

三.使用用例

创建测试页面
<template>
  <div class="cw-select">
    <select-v2
        :data="list"
        v-model="value"
        placeholder="请选择下拉数据"
        clearable
        multiple
        @change="selectChange"
    ></select-v2>
  </div>
</template>

<script>
import SelectV2 from '@/components/SelectV2'
export default {
  name: 'demo',
  components: {
    'select-v2': SelectV2
  },
  data() {
    return {
      list: [],
      // 下拉框选择的默认值,可以时字符串和数组
      value: []
    };
  },
  mounted() {
    this.list = [];
    // 创建20000条测试数据
    for (let i = 0; i < 20000; i++) {
      this.list.push({ value: i, label: '测试' + i + '' });
    }
  },
  methods: {
    selectChange(val) {
      console.log('下拉框选择的值', val);
    }
  }
};
</script>
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
你可以使用第三方插件 `vue-scroll-picker` 来实现滚动时间选择器效果。以下是使用步骤: 1. 安装 `vue-scroll-picker` 插件: ``` npm install vue-scroll-picker --save ``` 2. 在需要使用的组件中引入 `vue-scroll-picker`: ```javascript import VueScrollPicker from 'vue-scroll-picker'; ``` 3. 在模板中使用 `vue-scroll-picker`: ```html <vue-scroll-picker :data="data" v-model="selectedValue"> <template slot="item" slot-scope="{ item }"> {{ item.text }} </template> </vue-scroll-picker> ``` 其中,`data` 是一个数组,包含需要选择的时间范围,如: ```javascript data: [ { text: '00:00', value: '00:00' }, { text: '01:00', value: '01:00' }, { text: '02:00', value: '02:00' }, // ... { text: '23:00', value: '23:00' }, ] ``` `selectedValue` 是选中的时间值,可以在组件中通过 `v-model` 双向绑定。 4. 样式自定义 你可以通过 CSS 样式自定义滚动时间选择器。例如,以下是一个简单的样式示例: ```css .vue-scroll-picker { width: 100%; height: 200px; position: relative; overflow: hidden; margin: 0 auto; } .vue-scroll-picker-wrapper { width: 100%; height: 100%; position: absolute; top: 0; left: 0; display: flex; justify-content: center; align-items: center; } .vue-scroll-picker-item { font-size: 24px; line-height: 48px; color: #333; text-align: center; height: 48px; flex-shrink: 0; cursor: pointer; } ``` 以上就是使用 `vue-scroll-picker` 实现滚动时间选择器效果的简单步骤,你可以根据自己的需要进行进一步的样式和功能定制。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值