虚拟列表 ‘vue-virtual-scroller’ 使用指南

11,859 阅读3分钟

本文主要概述虚拟列表插件vue-birtual-scroll的一些使用心得,包括在element select 下拉框中的使用问题经验总结

官方文档地址:github.com/Akryum/vue-…

1. 安装包

npm i vue-virtual-scroller --save

2. vue2项目中使用

插件支持两种虚拟列表方法使用,一个是适用于固定高度的虚拟列表 { RecycleScroller },性能较高,占用的资源少速度快。

import { RecycleScroller } from 'vue-virtual-scroller'

import 'vue-virtual-scroller/dist/vue-virtual-scroller.css'

下面以下拉菜单为使用场景进行贴代码,业务场景就是后端一次性返回过多的商品数据列表,导致页面刚加载时一动不动,打开时也特别的卡。所以需要进行虚拟列表优化

image.png

<el-select
  v-model="selectedProduct"
  placeholder="选择讲解商品快速定位"
  class="ui-el-select mr10"
  popper-class="ui-poper-class"
  size="small"
  value-key="product_id"
  filterable
  remote
  :loading="filterLoading"
  :remote-method="remoteMethod"
  @change="handleProductChange"
  @visible-change="(visibel) => visibel && remoteMethod('')"
>
  <RecycleScroller
    v-slot="{ item }"
    ref="recycleScrollRef"
    class="relative h-full min-w-500"
    :item-size="34" <!-- 一列的高度单item高度 --> 
    :items="productFilterList" <!-- 绑定需要渲染的数组 --> 
    key-field="product_id" <!-- 唯一标识字段,不重复的id,没有的话item那边加个,index用index也行 --> 
  >
    <el-option :key="item.product_id" :label="item.product_title" :value="item" class="h-34">
      <div class="flex">
        <img :src="item.image_url" alt="" width="30" height="30" />
        <div class="relative ml-8 w-425 flex-1">
          <text-over-tips :context="item.product_title" :calculated-offset="0"></text-over-tips>
        </div>
      </div>
    </el-option>
  </RecycleScroller>
  <div v-if="!filterLoading && productFilterList.length == 0" class="text-center text-12 text-#fff">
    暂无数据
  </div>
</el-select>

这里由于使用的el-select中的filterable 下拉搜索 所以需要手动写remote-method进行数据刷新,否则会导致虚拟列表只隐藏对应项而列表中有大量空白

下面展示没有手动刷新数据时搜索的反应

<el-select
  v-model="selectedProduct"
  placeholder="选择讲解商品快速定位"
  class="ui-el-select mr10"
  popper-class="ui-poper-class"
  size="small"
  value-key="product_id"
  filterable
  @change="handleProductChange"
>
  <RecycleScroller
    v-slot="{ item }"
    ref="recycleScrollRef"
    class="relative h-full min-w-500"
    :item-size="34" <!-- 一列的高度单item高度 --> 
    :items="productFilterList" <!-- 绑定需要渲染的数组 --> 
    key-field="product_id"  <!-- 唯一标识字段,不重复的id --> 
  >
    <el-option :key="item.product_id" :label="item.product_title" :value="item" class="h-34">
      <div class="flex">
        <img :src="item.image_url" alt="" width="30" height="30" />
        <div class="relative ml-8 w-425 flex-1">
          <text-over-tips :context="item.product_title" :calculated-offset="0"></text-over-tips>
        </div>
      </div>
    </el-option>
  </RecycleScroller>
  <div v-if="!filterLoading && productFilterList.length == 0" class="text-center text-12 text-#fff">
    暂无数据
  </div>
</el-select>

在使用el-select 的下拉搜索功能时,没有使用自定义过滤会导致el-options隐藏了但是虚拟列表还占着位置,因为vue-virtual-scroller 的实现原理是写

image.png

偏移进行列表优化

image.png

手动刷新代码,跟element官网示例一样加loading有加载的样

 remoteMethod(query) {
  clearTimeout(this.filterTimeout)
  if (query !== '') {
    this.filterLoading = true
    this.filterTimeout = setTimeout(() => {
      this.productFilterList = this.productList.filter((item) => {
        return item.product_title.toLowerCase().indexOf(query.toLowerCase()) > -1
      })
      this.filterLoading = false
    }, 200)
  } else {
    this.filterLoading = false
    this.productFilterList = clone(this.productList)
  }
}

也支持虚拟列表前后插槽

 <RecycleScroller
    class="relative h-full w-520"
    :item-size="34" <!-- 一列的高度,单item高度 --> 
    :items="productFilterList" <!-- 绑定需要渲染的数组 --> 
    key-field="product_id"  <!-- 唯一标识字段,不重复的id --> 
  >
    <template #before> <!-- 虚拟列表循环前插槽 --> 
      <el-option label="全部商品" class="text-center" value="">全部商品</el-option>
    </template>
    <template #default="{ item }">
      <el-option :label="item.product_title" :value="item.product_id">
        <div class="flex">
          <img :src="item.image_url" alt="" width="30" height="30" />
          <div class="relative ml-8 w-440 flex-1">
            <text-over-tips :context="item.product_title" :calculated-offset="0"></text-over-tips>
          </div>

        </div>
      </el-option>
    </template>
  </RecycleScroller>
  <div
    v-if="!filterLoading && productFilterList.length == 0"
    class="h-40 w-520 text-center text-12 leading-40 text-#fff"
  >
    暂无数据
  </div>
</el-select>

# 不定高度时使用插件中另外一个组件 DynamicScroller,需要搭配 DynamicScrollerItem 组件使用,下面是使用案例

import { DynamicScroller, DynamicScrollerItem } from 'vue-virtual-scroller'
import 'vue-virtual-scroller/dist/vue-virtual-scroller.css'

export default {
  components: {
    DynamicScroller,
    DynamicScrollerItem
  }
}
<DynamicScroller 
    :items="list" <!-- 绑定数组 -->
    :min-item-size="180" <!-- 设置最小高度 -->
    class="scroller h-full">
  <template #default="{ item, active }">
    <DynamicScrollerItem :item="item" :active="active" :data-index="item.id"> <!-- :data-index 跟之前的key-field类似,跟vue for 的key一样 -->
     <!-- 这里放需要渲染的代码,以前写v-for 循环item 一样的写法 -->
    </DynamicScrollerItem>
  </template>
</DynamicScroller>

下面附上官网API翻译

image.png