VirtualScroll 虚拟滚动
基于 @tanstack/vue-virtual 封装的虚拟滚动组件,适用于大数据列表的高性能滚动场景。只渲染可视区域内的元素,大幅提升长列表的渲染性能。
注意: 该组件以动态导入方式依赖
@tanstack/vue-virtual,属于可选依赖。若未安装,组件会显示提示信息而非报错。安装方式:pnpm add @tanstack/vue-virtual
预览
基础用法(5000 条数据)
斑马纹列表(striped)
带边框列表(bordered)
滚动到指定索引(scrollToIndex)
安装
pnpm dlx brutx-vue@latest add virtual-scroll用法
<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>斑马纹列表
<VirtualScroll
:items="items"
variant="striped"
size="lg"
>
<template #default="{ item }">
<div class="p-4">{{ item.name }}</div>
</template>
</VirtualScroll>加载更多
<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>空状态
<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>带边框的列表
<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 | 带底部边框 |
尺寸
| 尺寸 | 最大高度 |
|---|---|
sm | max-h-64 (16rem) |
default | max-h-96 (24rem) |
lg | max-h-[32rem] |
xl | max-h-[48rem] |
full | max-h-full(依赖父容器提供确定高度,如 h-screen/h-96) |
数据类型
interface VirtualScrollItem {
id?: string | number
[key: string]: unknown
}程序化控制
通过 ref 引用组件后可调用以下方法(经 defineExpose 暴露):
<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 参考
| 名称 | 类型 | 默认值 | 说明 |
|---|---|---|---|
class | string | undefined | undefined | 追加到虚拟滚动根容器的自定义 CSS 类。 |
详情声明类型string | undefined 默认值详情
来源packages/ui/src/components/virtual-scroll/types.ts | |||
items必填 | T[] | — | 参与虚拟化的数据数组;其长度决定虚拟器中的条目总数。 |
详情 | |||
itemHeight | number | undefined | 48 | 固定高度模式下的每项估算高度,单位为像素;动态高度模式也使用它作为初始估算值。 |
详情声明类型number | undefined 默认值详情
来源packages/ui/src/components/virtual-scroll/types.ts | |||
dynamicHeight | boolean | undefined | false | 启用已挂载条目的实际高度测量;关闭时每项使用 itemHeight 固定高度。 |
详情声明类型boolean | undefined 字面量
默认值详情
来源packages/ui/src/components/virtual-scroll/types.ts | |||
size | "default" | "lg" | "sm" | "xl" | "full" | undefined | "default" | 容器尺寸变体;full 需要父容器提供确定高度。 |
详情声明类型"default" | "lg" | "sm" | "xl" | "full" | undefined 字面量
默认值详情
来源packages/ui/src/components/virtual-scroll/types.ts | |||
variant | "default" | "bordered" | "striped" | undefined | "default" | 选择列表项样式;striped 为索引奇数项添加交替背景,bordered 添加边框。 |
详情声明类型"default" | "bordered" | "striped" | undefined 字面量
默认值详情
来源packages/ui/src/components/virtual-scroll/types.ts | |||
overscan | number | undefined | 5 | 可视区域之外额外预渲染的条目数,传给虚拟器。 |
详情声明类型number | undefined 默认值详情
来源packages/ui/src/components/virtual-scroll/types.ts | |||
scrollEndThreshold | number | undefined | 50 | 距离滚动底部小于此像素数时触发 scroll-end。 |
详情声明类型number | undefined 默认值详情
来源packages/ui/src/components/virtual-scroll/types.ts | |||
role | string | undefined | "list" | 设置虚拟列表滚动容器的 ARIA role,默认 list。 |
详情声明类型string | undefined 默认值详情
来源packages/ui/src/components/virtual-scroll/types.ts | |||
itemRole | string | undefined | "listitem" | 设置每个虚拟列表项的 ARIA role,默认 listitem。 |
详情声明类型string | undefined 默认值详情
来源packages/ui/src/components/virtual-scroll/types.ts | |||
| 名称 | 签名或类型 | 说明 |
|---|---|---|
@scroll-end | [] | 滚动位置进入 scrollEndThreshold 指定的底部范围时发出;离开该范围后再次进入可重新触发。 |
详情声明类型[] 来源packages/ui/src/components/virtual-scroll/VirtualScroll.vue | ||
@scroll | [scrollTop: number] | 滚动容器发生滚动时发出当前 scrollTop,单位为像素。 |
详情声明类型[scrollTop: number] 来源packages/ui/src/components/virtual-scroll/VirtualScroll.vue | ||
| 名称 | 签名或类型 | 说明 |
|---|---|---|
#loading | {} | 提供加载状态内容;虚拟器动态导入加载时和非空列表就绪后的底部都会渲染该插槽。 |
详情声明类型{}来源packages/ui/src/components/virtual-scroll/VirtualScroll.vue | ||
#empty | {} | items 为空时替换空状态文案;仅空列表分支渲染。
|
详情声明类型{}来源packages/ui/src/components/virtual-scroll/VirtualScroll.vue | ||
#default | { item: T; index: number; } | 渲染每个可见虚拟条目;作用域提供原始 item 和其零基 index。 |
详情声明类型{ item: T; index: number; }来源packages/ui/src/components/virtual-scroll/VirtualScroll.vue | ||
| 名称 | 签名或类型 | 说明 |
|---|---|---|
scrollToIndex | (index: number) => void | 滚动到指定条目索引;索引会限制在现有范围内,空列表或虚拟器尚不可用时不执行。 |
详情声明类型(index: number) => void 来源packages/ui/src/components/virtual-scroll/VirtualScroll.vue | ||
measureElement | (el: Element | null) => void | 将指定 DOM 元素交给虚拟器测量;元素为 null 或虚拟器未就绪时不执行。 |
详情声明类型(el: Element | null) => void 来源packages/ui/src/components/virtual-scroll/VirtualScroll.vue | ||
measure | () => void | 重新测量所有已挂载条目的尺寸,适用于内容高度变化后。 |
详情声明类型() => void 来源packages/ui/src/components/virtual-scroll/VirtualScroll.vue | ||
virtualizer | ShallowRef<VirtualizerInstance | null, VirtualizerInstance | null> | 底层 TanStack Virtualizer 的浅层响应式引用;依赖加载完成前或加载失败时引用值为 null。 |
详情声明类型ShallowRef<VirtualizerInstance | null, VirtualizerInstance | null> 字面量
关联类型 VirtualizerInstance 类型定义 — VirtualizerInstanceexport interface VirtualizerInstance {
getVirtualItems: () => VirtualizerVirtualItem[]
getTotalSize: () => number
scrollToIndex: (index: number) => void
measure: () => void
measureElement: (el: Element | null) => void
}来源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 值来控制触发的灵敏度。