VirtualList 虚拟列表
只渲染可见行的固定行高长列表
用法
基础
itemHeight 固定行高,height 设定视口高度。
示例加载中...
稳定的 key
<template>
<TxVirtualList :items="users" :item-height="44" height="360px" item-key="id">
<template #item="{ item }">
<UserRow :user="item" />
</template>
</TxVirtualList>
</template>
命令式滚动
<script setup lang="ts">
const listRef = ref()
function jumpToLatest() {
listRef.value?.scrollToBottom()
}
</script>
<template>
<TxButton @click="jumpToLatest">最新</TxButton>
<TxVirtualList ref="listRef" :items="logs" :item-height="32" :height="400" />
</template>
最佳实践
- 只用于固定行高;变高内容会让滚动计算失真。
- 对象列表用
itemKey指向稳定 id;只有不可变数组才适合用索引作 key。 overscan保持适中:越大快速滚动越平滑,DOM 开销也越大。- 行间距放进固定高度的行内,不要给行加垂直 margin。
scroll只用于统计或懒加载触发,不要做逐帧重计算。
API 参考
属性
| 属性名 | 类型 | 默认值 | 说明 |
|---|---|---|---|
items | T[] | [] | 完整数据集。 |
itemHeight | number | 必填 | 固定行高(px),所有行必须一致。 |
height | number | string | 320 | 滚动容器高度;数字转为 px。 |
overscan | number | 4 | 视口前后额外渲染的行数。 |
itemKey | keyof T | (item: T, index: number) => string | number | index | 行 key:字段名或函数;字段缺失时回退为该项在 items 中的索引。 |
事件
| 事件名 | 参数 | 说明 |
|---|---|---|
scroll | { scrollTop: number, startIndex: number, endIndex: number } | 滚动并更新可见区间后触发。 |
插槽
| 插槽名 | Props | 说明 |
|---|---|---|
item | { item: T, index: number } | 渲染每个可见项;默认把 item 渲染为文本。 |
暴露方法
| 方法 | 签名 | 说明 |
|---|---|---|
scrollToIndex | (index: number) => void | 滚动到 Math.max(0, index) * itemHeight;不限制超出末项的索引。 |
scrollToTop | () => void | 滚动到顶部。 |
scrollToBottom | () => void | 滚动到 max(0, totalHeight - viewHeight)。 |
概述
- 每行固定为
itemHeight像素高;数字height转为 px,字符串原样应用。 - 数字、px 与纯数字字符串高度立即参与计算;
%、vh、vw、rem、em等到挂载后读取clientHeight,SSR 或隐藏容器应给出具体高度。 - 存在
ResizeObserver时,容器尺寸变化会更新视口高度。 - 可见区间从
floor(scrollTop / itemHeight) - overscan到视口末端再加overscan,限制在0与items.length之间。 - 占位高度为
items.length * itemHeight,可见项按startIndex * itemHeight位移。 - 容器与行不带
role或aria-*。需要列表语义时由外层补role="list"/role="listitem",aria-setsize/aria-posinset使用真实总数与绝对索引。
技术实现
- 源码:
packages/tuffex/packages/components/src/virtual-list/。
查看源码
packages/tuffex/packages/components/src/virtual-list/index.ts