Skip to content

VirtualScroll 虚拟滚动 ​

基于 @tanstack/vue-virtual 封装的虚拟滚动组件,适用于大数据列表的高性能滚动场景。只渲染可视区域内的元素,大幅提升长列表的渲染性能。

注意: 该组件以动态导入方式依赖 @tanstack/vue-virtual,属于可选依赖。若未安装,组件会显示提示信息而非报错。安装方式:pnpm add @tanstack/vue-virtual

预览 ​

Preview

基础用法(5000 条数据)

斑马纹列表(striped)

带边框列表(bordered)

滚动到指定索引(scrollToIndex)

安装 ​

pnpm dlx brutx-vue@latest add virtual-scroll

用法 ​

vue
<script setup>
import { VirtualScroll } from 'brutx-ui-vue'

const items = Array.from({ length: 10000 }, (_, i) => ({
    id: i,
    name: `项目 ${i + 1}`,
}))
</script>

<template>
    <VirtualScroll :items="items" :item-height="48">
        <template #default="{ item, index }">
            <div class="p-4 border-b-2 border-brutal">
                {{ item.name }}
            </div>
        </template>
    </VirtualScroll>
</template>

斑马纹列表 ​

vue
<VirtualScroll
    :items="items"
    variant="striped"
    size="lg"
>
    <template #default="{ item }">
        <div class="p-4">{{ item.name }}</div>
    </template>
</VirtualScroll>

加载更多 ​

vue
<VirtualScroll
    :items="items"
    @scroll-end="loadMore"
>
    <template #default="{ item }">
        <div class="p-4">{{ item.name }}</div>
    </template>
    <template #loading>
        <div class="p-4 text-center">加载中...</div>
    </template>
</VirtualScroll>

空状态 ​

vue
<VirtualScroll :items="[]">
    <template #empty>
        <div class="p-8 text-center">
            <Icon name="empty" class="w-16 h-16 mx-auto mb-4" />
            <p>暂无数据</p>
        </div>
    </template>
</VirtualScroll>

带边框的列表 ​

vue
<VirtualScroll
    :items="items"
    variant="bordered"
    :item-height="64"
>
    <template #default="{ item }">
        <div class="flex items-center gap-4 p-4">
            <Avatar :src="item.avatar" />
            <div>
                <p class="font-bold">{{ item.name }}</p>
                <p class="text-sm text-muted">{{ item.email }}</p>
            </div>
        </div>
    </template>
</VirtualScroll>

变体 ​

变体说明
default默认样式
striped斑马纹(偶数行背景色)
bordered带底部边框

尺寸 ​

尺寸最大高度
smmax-h-64 (16rem)
defaultmax-h-96 (24rem)
lgmax-h-[32rem]
xlmax-h-[48rem]
fullmax-h-full(依赖父容器提供确定高度,如 h-screen/h-96)

数据类型 ​

ts
interface VirtualScrollItem {
    id?: string | number
    [key: string]: unknown
}

程序化控制 ​

通过 ref 引用组件后可调用以下方法(经 defineExpose 暴露):

vue
<script setup>
import { ref } from 'vue'
import { VirtualScroll } from 'brutx-ui-vue'

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

function jumpToMiddle() {
    listRef.value?.scrollToIndex(Math.floor(items.length / 2))
}
</script>

<template>
    <Button @click="jumpToMiddle">跳转到中间</Button>
    <VirtualScroll ref="listRef" :items="items" :item-height="48">
        <template #default="{ item }">
            <div class="p-4">{{ item.name }}</div>
        </template>
    </VirtualScroll>
</template>

API 参考 ​

VirtualScroll — 属性
名称类型默认值说明
class
string | undefined
undefined
追加到虚拟滚动根容器的自定义 CSS 类。
详情

声明类型

string | undefined

默认值详情

解析值
undefined

来源

types.ts:22

packages/ui/src/components/virtual-scroll/types.ts

items
必填
T[]
—
参与虚拟化的数据数组;其长度决定虚拟器中的条目总数。
详情

声明类型

T[]

默认值详情

未声明默认值
—

来源

types.ts:8

packages/ui/src/components/virtual-scroll/types.ts

itemHeight
number | undefined
48
固定高度模式下的每项估算高度,单位为像素;动态高度模式也使用它作为初始估算值。
详情

声明类型

number | undefined

默认值详情

解析值
48

来源

types.ts:10

packages/ui/src/components/virtual-scroll/types.ts

dynamicHeight
boolean | undefined
false
启用已挂载条目的实际高度测量;关闭时每项使用 itemHeight 固定高度。
详情

声明类型

boolean | undefined

字面量

  • false
  • true

默认值详情

解析值
false

来源

types.ts:12

packages/ui/src/components/virtual-scroll/types.ts

size
"default" | "lg" | "sm" | "xl" | "full" | undefined
"default"
容器尺寸变体;full 需要父容器提供确定高度。
详情

声明类型

"default" | "lg" | "sm" | "xl" | "full" | undefined

字面量

  • "default"
  • "full"
  • "lg"
  • "sm"
  • "xl"

默认值详情

解析值
"default"

来源

types.ts:14

packages/ui/src/components/virtual-scroll/types.ts

variant
"default" | "bordered" | "striped" | undefined
"default"
选择列表项样式;striped 为索引奇数项添加交替背景,bordered 添加边框。
详情

声明类型

"default" | "bordered" | "striped" | undefined

字面量

  • "bordered"
  • "default"
  • "striped"

默认值详情

解析值
"default"

来源

types.ts:16

packages/ui/src/components/virtual-scroll/types.ts

overscan
number | undefined
5
可视区域之外额外预渲染的条目数,传给虚拟器。
详情

声明类型

number | undefined

默认值详情

解析值
5

来源

types.ts:18

packages/ui/src/components/virtual-scroll/types.ts

scrollEndThreshold
number | undefined
50
距离滚动底部小于此像素数时触发 scroll-end。
详情

声明类型

number | undefined

默认值详情

解析值
50

来源

types.ts:20

packages/ui/src/components/virtual-scroll/types.ts

role
string | undefined
"list"
设置虚拟列表滚动容器的 ARIA role,默认 list。
详情

声明类型

string | undefined

默认值详情

解析值
"list"

来源

types.ts:24

packages/ui/src/components/virtual-scroll/types.ts

itemRole
string | undefined
"listitem"
设置每个虚拟列表项的 ARIA role,默认 listitem。
详情

声明类型

string | undefined

默认值详情

解析值
"listitem"

来源

types.ts:26

packages/ui/src/components/virtual-scroll/types.ts

VirtualScroll — 事件
名称签名或类型说明
@scroll-end
[]
滚动位置进入 scrollEndThreshold 指定的底部范围时发出;离开该范围后再次进入可重新触发。
详情

声明类型

[]

来源

VirtualScroll.vue

packages/ui/src/components/virtual-scroll/VirtualScroll.vue

@scroll
[scrollTop: number]
滚动容器发生滚动时发出当前 scrollTop,单位为像素。
详情

声明类型

[scrollTop: number]

来源

VirtualScroll.vue

packages/ui/src/components/virtual-scroll/VirtualScroll.vue

VirtualScroll — 插槽
名称签名或类型说明
#loading
{}
提供加载状态内容;虚拟器动态导入加载时和非空列表就绪后的底部都会渲染该插槽。
详情

声明类型

{}

来源

VirtualScroll.vue

packages/ui/src/components/virtual-scroll/VirtualScroll.vue

#empty
{}
items 为空时替换空状态文案;仅空列表分支渲染。
  • 未提供插槽内容时显示 virtualScroll.empty:“暂无数据”。
详情

声明类型

{}

来源

VirtualScroll.vue

packages/ui/src/components/virtual-scroll/VirtualScroll.vue

#default
{ item: T; index: number; }
渲染每个可见虚拟条目;作用域提供原始 item 和其零基 index。
详情

声明类型

{ item: T; index: number; }

来源

VirtualScroll.vue

packages/ui/src/components/virtual-scroll/VirtualScroll.vue

VirtualScroll — 暴露成员
名称签名或类型说明
scrollToIndex
(index: number) => void
滚动到指定条目索引;索引会限制在现有范围内,空列表或虚拟器尚不可用时不执行。
详情

声明类型

(index: number) => void

来源

VirtualScroll.vue

packages/ui/src/components/virtual-scroll/VirtualScroll.vue

measureElement
(el: Element | null) => void
将指定 DOM 元素交给虚拟器测量;元素为 null 或虚拟器未就绪时不执行。
详情

声明类型

(el: Element | null) => void

来源

VirtualScroll.vue

packages/ui/src/components/virtual-scroll/VirtualScroll.vue

measure
() => void
重新测量所有已挂载条目的尺寸,适用于内容高度变化后。
详情

声明类型

() => void

来源

VirtualScroll.vue

packages/ui/src/components/virtual-scroll/VirtualScroll.vue

virtualizer
ShallowRef<VirtualizerInstance | null, VirtualizerInstance | null>
底层 TanStack Virtualizer 的浅层响应式引用;依赖加载完成前或加载失败时引用值为 null。
详情

声明类型

ShallowRef<VirtualizerInstance | null, VirtualizerInstance | null>

字面量

  • null

关联类型

VirtualizerInstance
类型定义 — VirtualizerInstance
export interface VirtualizerInstance {
    getVirtualItems: () => VirtualizerVirtualItem[]
    getTotalSize: () => number
    scrollToIndex: (index: number) => void
    measure: () => void
    measureElement: (el: Element | null) => void
}

来源

VirtualScroll.vue

packages/ui/src/components/virtual-scroll/VirtualScroll.vue

可访问性 ​

  • ARIA 属性:使用 role="list" 和 role="listitem" 语义化标记;支持 aria-setsize 和 aria-posinset 属性
  • 键盘操作:支持 aria-label 国际化标签

常见问题 ​

Q: 列表项高度不固定时,虚拟滚动会出现跳动怎么办?

A: itemHeight 是预估值,用于计算滚动位置。如果列表项高度差异较大,可能导致滚动时出现跳动。建议将 itemHeight 设置为大多数列表项的平均高度,或者通过 CSS 确保所有列表项高度一致。适当增大 overscan 值也可以缓解跳动感。

Q: 安装后组件显示提示信息而非正常渲染,是什么原因?

A: VirtualScroll 依赖 @tanstack/vue-virtual,属于可选依赖。如果未安装该包,组件会显示安装提示而非报错。请执行 pnpm add @tanstack/vue-virtual 安装依赖后重新启动项目。

Q: 滚动到底部时 scroll-end 事件没有触发?

A: 请检查是否正确提供了数据源。scroll-end 事件需要在容器实际发生滚动且到达底部时才会触发。如果数据量不足以产生滚动条(列表高度未超出容器),则不会触发。可以调整 scrollEndThreshold 值来控制触发的灵敏度。

蛮力铸就。