Vue2 - vue-virtual-scroller 长列表优化原理

vue-virtual-scroller

1,效果展示

在这里插入图片描述

1w 条数据无压力,看下初始渲染时间 Rendering 对比:

在这里插入图片描述
在这里插入图片描述

2,原理

目标:只加载在可视容器中的列表项。

实现:

2.1,滚动条的处理

  1. 为了保证滚动条正确显示和滑动,需要按照原本的数据量来计算滑动内容区域 wrapper 的真实高度,
  2. wrapper 外层的 container 作为滑动容器,设置 overflow:auto + 定高(比如500px)。

2.2,展示内容处理

最终目标:只展示可视区域内的 item,所以肯定会对源数据进行截取,需要计算出 startIndexendIndex

  1. 为了保证只显示可视区域内的 item,可通过定位将每个 item 的初始位置都固定在第一行(top: 0; left: 0)。
  2. 在对每个 item 设置不同的偏移量 transform: translateY(index * itemHeight); 就可以正常展示了。
  3. 滑动时,需要根据滑动的距离 container.scrollTop 来重新计算 startIndex。再加上 container.clientHeight 可以计算出 endIndex
  4. 则可得出初始偏移量:transform: translateY(startIndex* itemHeight);

3,实现

<!-- 父组件 -->
<template>
  <div>
    <RecycleScroller :items="list" :itemSize="54" v-slot="{ item }" class="scroller">
      <div class="item-box">
        <span>{
  { item.id }}</span>
        <span>{
  { item.name }}</span>
      </div>
    </RecycleScroller>
  </div>
</template>

<script>
import RecycleScroller from './components/RecycleScroller.vue'

const arr = []
for (let index = 0; index < 10000; index++) {
     
  arr[index] = {
     
    id: 'id' + index,
    name: `name` + index
  }
}

export default {
     
  components: {
     
    RecycleScroller
  },
  data() {
     
    return {
     
      list: arr
    }
  }
}
</script>
<style>
.scroller {
     
  width: 200px;
  height: 500px;
  overflow: auto;
}

.item-box {
     
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 15px;
}
</style>
<!-- RecycleScroller.vue -->
<template>
  <div class="recycle-container" ref="container" @scroll="setPool">
    <div class="recycle-wrapper" :style="{ height: totalSize }">
      <div v-for="poolItem in pool" :key="poolItem.keyField" class="recycle-item" :style="{ transform: `translateY(${poolItem.position}px)` }">
        <slot :item="poolItem.item"></slot>
      </div>
    </div>
  </div>
</template>

<script>
export default {
     
  props: {
     
    items: {
     
      // 数据列表
      type: Array,
      default: () => []
    },
    itemSize: {
     
      // 每条数据的高度
      type: Number,
      default: 50
    },
    keyField: {
     
      // items 中的唯一标识作为 key
      type: String,
      default: 'id'
    }
  },
  data() {
     
    return {
     
      pool: [] // 会被渲染的列表内容
    }
  },
  computed: {
     
    totalSize() {
     
      return this.items.length * this.itemSize + 'px'
    }
  },
  methods: {
     
    setPool() {
     
      const scrollTop = this.$refs.container.scrollTop
      const clientHeight = this.$refs.container.clientHeight
      
      let startIndex = Math.floor(scrollTop / this.itemSize) || 0
      let endIndex = Math.ceil((scrollTop + clientHeight) / this.itemSize)
      const startPosition = startIndex * this.itemSize
      
      // 每次都重新计算 item 对应的位置。
      this.pool = this.items.slice(startIndex, endIndex).map((item, index) => ({
     
        item,
        position: startPosition + this.itemSize * index
      }))
    }
  },
  mounted() {
     
    this.setPool()
  }
}
</script>

<style scoped>
.recycle-container {
     
  overflow: auto;
}
.recycle-wrapper {
     
  position: relative;
}
.recycle-item {
     
  position: absolute;
  width: 100%;
  top: 0;
  left: 0;
}
</style>

如果担心滑动太快导致的白屏问题(没有计算渲染出来),可以在前后各增加10条数据,一般就没有问题了。

以上。

相关推荐

  1. Vue2学习笔记(列表渲染)

    2024-01-23 10:34:01       51 阅读
  2. 11-4.Vue2.x基本列表列表更新—push

    2024-01-23 10:34:01       33 阅读
  3. 11-3.Vue2.x基本列表列表排序—sort

    2024-01-23 10:34:01       40 阅读
  4. Vue2 文本展示和收起

    2024-01-23 10:34:01       59 阅读
  5. vue2获取视频时

    2024-01-23 10:34:01       25 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-01-23 10:34:01       98 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-01-23 10:34:01       106 阅读
  3. 在Django里面运行非项目文件

    2024-01-23 10:34:01       87 阅读
  4. Python语言-面向对象

    2024-01-23 10:34:01       96 阅读

热门阅读

  1. 编程笔记 html5&css&js 052 CSS伪类

    2024-01-23 10:34:01       57 阅读
  2. NLP自然语言处理原理应用讲解

    2024-01-23 10:34:01       59 阅读
  3. 自然语言处理的发展

    2024-01-23 10:34:01       58 阅读
  4. krpano制作无水印360°场景方法

    2024-01-23 10:34:01       53 阅读
  5. P1697 [USACO18JAN] Lifeguards B 题解

    2024-01-23 10:34:01       47 阅读
  6. 面试题(三)mybatis

    2024-01-23 10:34:01       52 阅读
  7. #Uniapp: onReachBottom页面触底的事件回调

    2024-01-23 10:34:01       58 阅读
  8. ERC20 解读

    2024-01-23 10:34:01       59 阅读
  9. #Uniapp:uni.request(OBJECT)

    2024-01-23 10:34:01       59 阅读
  10. C#6-10新增的内容

    2024-01-23 10:34:01       65 阅读
  11. Spring Boot 项目请求参数丢失问题排查与解决

    2024-01-23 10:34:01       52 阅读
  12. MySQL运维实战(4.6) SQL_MODE之NO_BACKSLASH_ESCAPES

    2024-01-23 10:34:01       64 阅读