Skip to content

v-virtual-list

使用虚拟滚动高效渲染大型列表。仅渲染可见项以获得最佳性能。

起始版本: 1.3.0

用法

基本

vue
<template>
  <div v-virtual-list="{
    items: largeList,
    itemHeight: 50,
    height: 400
  }">
    <template #default="{ item, index }">
      <div class="list-item">{{ index }}: {{ item.name }}</div>
    </template>
  </div>
</template>

API

类型

typescript
interface VirtualListOptions<T = any> {
  items: T[]
  itemHeight: number | ((index: number) => number)
  height: number
  buffer?: number // 默认: 5
  keyField?: string // 默认: 'id'
  direction?: 'vertical' | 'horizontal' // 默认: 'vertical'
  onScroll?: (event: Event) => void
  onResize?: (size: { width: number; height: number }) => void
}

选项

选项类型默认值描述
itemsT[]-要渲染的数组(必填)
itemHeightnumber | (index) => number-每项高度(必填)
heightnumber-容器高度(必填)
buffernumber5额外渲染的项目数
keyFieldstring'id'用作唯一键的字段
direction'vertical' | 'horizontal''vertical'滚动方向
onScroll(event) => void-滚动事件处理程序
onResize(size) => void-尺寸变化事件处理程序

Composable 用法

你也可以使用 useVirtualList composable 实现相同功能:

vue
<script setup>
import { ref } from 'vue'
import { useVirtualList } from 'directix'

const items = ref(Array.from({ length: 10000 }, (_, i) => ({ id: i, name: `项目 ${i}` })))

const {
  visibleItems,
  totalHeight,
  containerRef,
  listStyle,
  scrollToIndex
} = useVirtualList({
  items,
  itemSize: 50,
  height: 600
})
</script>

<template>
  <div ref="containerRef" :style="listStyle">
    <div :style="{ height: totalHeight + 'px', position: 'relative' }">
      <div
        v-for="{ item, index, style } in visibleItems"
        :key="item.id"
        :style="style"
      >
        {{ item.name }}
      </div>
    </div>
  </div>
</template>

API

typescript
type ItemSizeFunction = (index: number) => number

interface UseVirtualListOptions<T = any> {
  /** 要渲染的数组(必填) */
  items: Ref<T[]> | T[]
  /** 每项的高度(像素),可以是固定值或函数 @default 50 */
  itemSize?: number | ItemSizeFunction | Ref<number | ItemSizeFunction>
  /** 容器高度(像素) @default 400 */
  height?: number | Ref<number>
  /** 可见区域外额外渲染的项目数 @default 3 */
  overscan?: number | Ref<number>
  /** 项目键字段名 @default 'id' */
  keyField?: string
}

interface VirtualListItem<T = any> {
  /** 项目数据 */
  item: T
  /** 原始列表中的索引 */
  index: number
  /** 定位样式 */
  style: {
    position: string
    top: string
    height: string
    width: string
  }
}

interface UseVirtualListReturn<T = any> {
  /** 当前可见的项目 */
  visibleItems: Ref<VirtualListItem<T>[]>
  /** 列表总高度 */
  totalHeight: Ref<number>
  /** 当前滚动位置 */
  scrollTop: Ref<number>
  /** 可见项目起始索引 */
  startIndex: Ref<number>
  /** 可见项目结束索引 */
  endIndex: Ref<number>
  /** 滚动到指定索引 */
  scrollToIndex: (index: number) => void
  /** 滚动到指定位置 */
  scrollTo: (scrollTop: number) => void
  /** 容器 ref */
  containerRef: Ref<HTMLElement | null>
  /** 列表样式 */
  listStyle: Ref<{ height: string, overflow: string, position: string }>
}

示例

大数据列表

vue
<template>
  <div
    v-virtual-list="{
      items: items,
      itemHeight: 40,
      height: 500,
      buffer: 10
    }"
    class="virtual-list"
  >
    <template #default="{ item }">
      <div class="item">
        <img :src="item.avatar" />
        <span>{{ item.name }}</span>
      </div>
    </template>
  </div>
</template>

<script setup>
const items = Array.from({ length: 10000 }, (_, i) => ({
  id: i,
  name: `用户 ${i}`,
  avatar: `https://i.pravatar.cc/40?img=${i % 70}`
}))
</script>

动态高度项

vue
<template>
  <div v-virtual-list="{
    items: messages,
    itemHeight: (index) => estimateHeight(messages[index]),
    height: 600
  }">
    <template #default="{ item }">
      <div class="message">{{ item.text }}</div>
    </template>
  </div>
</template>

水平滚动

vue
<template>
  <div v-virtual-list="{
    items: images,
    itemHeight: 200,
    height: 200,
    direction: 'horizontal'
  }">
    <template #default="{ item }">
      <img :src="item.url" />
    </template>
  </div>
</template>

基于 MIT 许可发布