Vue中 引入使用 vue-virtual-scroll-list 通过虚拟列表滚动加载,解决数据量过多时 页面卡顿、体验差的问题

3,619 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第3天,点击查看活动详情

1. 简介

vue-virtual-scroll-list npm 地址

vue-virtual-scroll-list 主页

安装

npm i vue-virtual-scroll-list

对比

通过 v-for 渲染 10000 个节点,当页面展示94~100区间内的数据时,我们发现浏览器实际将 10000 条数据全部渲染出来了,这很容易导致页面卡顿。

在这里插入图片描述

通过 vue-virtual-scroll-list 渲染 10000 个节点,当页面展示94~100区间内的数据时,我们发现浏览器实际只渲染了展示数据前后的91-110内的20条数据(取决于keeps参数)。

在这里插入图片描述

2. 简单使用

父组件

<template>
  <div style="height: 650px;">
    <virtual-list 
      style="height: 100%; overflow-y: auto;"
      :data-key="'id'"
      :data-sources="list"
      :data-component="itemComponent"
      :keeps="20"
      :extra-props="{
        address,
        basisInfo,
      }"
      @tobottom="listToBottom"/>
  </div>
</template>

<script>
import VirtualList from 'vue-virtual-scroll-list';
import itemComponent from './itemComponent';
export default {
  data(){
    return {
      VirtualList,
      itemComponent,
      list: [],
      address:{ province:'江苏',city:'南京' },
      basisInfo:[{age:20}] 
    }
  },
  components: {},
  methods:{
    content(){
      for(let i = 0; i < 10000; i++){
        const obj = {id: i, name: `人员${i}`};
        this.list.push(obj) 
      }
    },
    listToBottom(){
      console.log('到底了哦');
    },
  },
  mounted(){
    this.content()
  }
}
</script>

子组件

<template>
  <div>
    编号:{{ index }} <br/>
    姓名:{{ source.name }}<br/> 
    年龄:{{basisInfo[0].age}}<br/>
    地址:{{address.province}}-{{address.city}}
    <hr/>
  </div>
</template>

<script>
  export default {
    name: 'item-component',
    props: {
      // index of current item
      index: { 
        type: Number
      },
      source: { 
        type: Object,
        default () {
          return {}
        }
      },
      address:{
        type: Object,
        default () {
          return {}
        }
      },
      basisInfo:{
        type: Array,
        default () {
          return []
        }  
      }
    },
    mounted(){}
  }
</script>

3. 参数详解

必选参数

参数类型说明
data-keyString / Function唯一键来自每个数据对象中的 data-sources。或者使用每个 data-sources 调用的函数并返回它们的唯一键。它的值在 data-sources 中必须是唯一的,用于标识节点大小。
data-sourcesArray[Object]列表构建的源数组,每个数组数据必须是一个对象,并且有一个唯一的 key 或 generate for data-key 属性。
data-componentComponent由 vue 创建 / 声明的渲染项组件,将使用 data-sources 中的数据对象作为渲染 prop 并命名为:source。

可选参数

常用

参数类型默认值说明
keepsNumber30虚拟列表在真实 dom 中保持渲染的节点数量。
extra-propsObject{ }额外的参数(不在 data-sources 中的)分配给节点组件。注意:index 和 source 都已被占用。
estimate-sizeNumber50每个节点的预计尺寸,如果更接近平均尺寸,滚动条长度看起来更准确。建议:分配自己计算的平均值。

不常用

参数类型默认值说明
startNumber0设置滚动位置停留开始索引。
offsetNumber0设置滚动位置保持偏移。
scrollEvent滚动时发出,回调参数(event, range)
totopEvent滚动到顶部或左侧时发出,无参数。
tobottomEvent滚动到底部或右侧时发出,无参数。
resizedEvent调整项目大小时发出 (mounted),回调参数 (id, size)。
directionStringvertical滚动方向,可用值为 vertical 和 horizontal。
page-modeBooleanfalse设置虚拟列表使用全局文档滚动列表。
top-thresholdNumber0触发 totop 事件的阈值,注意:多次调用
bottom-thresholdNumber0触发 tobottom 事件的阈值,注意:多次调用
root-tagStringdiv根元素标记名称。
wrap-tagStringdiv包裹元素(role = item)标签名称。
item-classString包裹元素类名。
item-class-addFunction可将额外的类(字符串)返回到节点包裹元素参数(索引)的函数。
item-styleObject{ }节点包裹元素内联样式。
item-scoped-slotsObject{ }节点组件的 $scopedSlots。
header-tagStringdiv对于使用头槽,头槽包裹元素(role = header)标签名称。
header-classString{ }对于使用头槽,头槽包裹元素类名。
header-styleObject{ }对于使用头槽,头槽包裹元素内联样式。
footer-tagStringdiv对于使用页脚槽,页脚槽包裹元素(role = footer)标签名称。
footer-classStringdiv对于使用页脚槽,页脚槽包裹元素类名。
footer-styleObject{ }用于使用页脚槽、页脚槽包裹元素内联样式。

4. 公共方法

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

方法说明
reset()将所有状态重置回初始状态。
scrollToBottom()手动将滚动位置设置为底部。
scrollToIndex(index)手动将滚动位置设置为指定的索引。
scrollToOffset(offset)手动将滚动位置设置为指定的偏移量。
getSize(id)通过 id 获取指定的 item 大小(来自 data-key value)。
getSizes()获取存储(渲染)节点的总数。
getOffset()获取当前滚动偏移量。
getClientSize()获取包裹元素视口大小(宽度或高度)。
getScrollSize()获取所有滚动大小(scrollHeight 或 scrollWidth)。
updatePageModeFront()当使用页面模式和虚拟列表根元素 offsetTop 或 offsetLeft 变化时,需要手动调用该方法。

5. 注意

该组件使用就地补丁策略来呈现列表而不是 v-for 和 :key。

这种方式实现了最佳效率,但仅适用于列表输出不依赖于节点组件内部状态或临时 DOM 状态(如:表单输入值)的情况。

但是遇到这样的情况怎么办

在不维护内部状态的情况下,推荐使用 props 和 dispatch(无状态组件),这里是一个 keep-state 的例子。