Skip to content

组件

vue
<template>
  <div
    ref="container"
    class="overflow-y-auto border"
    :style="{ height: `${props.sliceSize * props.itemsSize}px` }"
    @scroll="onScroll"
  >
    <div :style="{ height: `${totalHeight}px` }">
      <div :style="{ transform: `translateY(${offsetY}px)` }">
        <div
          v-for="(item, index) in visibleItems"
          :key="startIndex + index"
          :style="{
            height: `${props.itemsSize}px`,
            lineHeight: `${props.itemsSize}px`
          }"
          class="border-b"
        >
          {{ item }}
        </div>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, computed } from 'vue'

const props = withDefaults(
  defineProps<{
    items: any[]
    itemsSize: number
    sliceSize?: number
  }>(),
  {
    sliceSize: 10
  }
)

const scrollTop = ref(0)
const container = ref(null)

const totalHeight = computed(() => props.items.length * props.itemsSize)
const startIndex = computed(() => Math.floor(scrollTop.value / props.itemsSize))
const endIndex = computed(() =>
  Math.min(props.items.length - 1, startIndex.value + props.sliceSize + 2)
)
const offsetY = computed(() => startIndex.value * props.itemsSize)
const visibleItems = computed(() =>
  props.items.slice(startIndex.value, endIndex.value + 1)
)

const onScroll = e => {
  scrollTop.value = e.target.scrollTop
}
</script>

使用

vue
<template>
  <div>
    <h2>虚拟列表示例(Vue)</h2>
    <VirtualList :items="items" :items-size="40" />
  </div>
</template>

<script setup>
import VirtualList from '@/components/VirtualList.vue'

const items = new Array(100).fill(null).map((_, i) => `Item ${i + 1}`)
// const items = Array.from({ length: 100 }, (_, i) => `Item ${i + 1}`)
</script>