VirtualList
A fixed-row-height list that renders only the visible rows.
Usage
Basic
itemHeight fixes the row height and height sets the viewport.
Loading demo...
Stable Item Keys
<template>
<TxVirtualList :items="users" :item-height="44" height="360px" item-key="id">
<template #item="{ item }">
<UserRow :user="item" />
</template>
</TxVirtualList>
</template>
Imperative Scrolling
<script setup lang="ts">
const listRef = ref()
function jumpToLatest() {
listRef.value?.scrollToBottom()
}
</script>
<template>
<TxButton @click="jumpToLatest">Latest</TxButton>
<TxVirtualList ref="listRef" :items="logs" :item-height="32" :height="400" />
</template>
Best Practices
- Use it only for fixed-height rows; variable heights throw off the scroll math.
- Point
itemKeyat a stable id for object lists; index keys suit immutable arrays only. - Keep
overscanmodest: more rows scroll smoother but cost more DOM work. - Put spacing inside the fixed-height row, not as vertical margins around it.
- Use
scrollfor analytics or lazy-load triggers, not per-frame heavy work.
API Reference
Props
| Prop | Type | Default | Description |
|---|---|---|---|
items | T[] | [] | The full data set. |
itemHeight | number | required | Fixed row height in px; every row must match it. |
height | number | string | 320 | Scroll container height; numbers become px. |
overscan | number | 4 | Extra rows rendered before and after the viewport. |
itemKey | keyof T | (item: T, index: number) => string | number | index | Row key: a field name or function; a missing field falls back to its index in items. |
Events
| Event | Payload | Description |
|---|---|---|
scroll | { scrollTop: number, startIndex: number, endIndex: number } | Fires after a scroll updates the visible range. |
Slots
| Slot | Props | Description |
|---|---|---|
item | { item: T, index: number } | Renders each visible item; defaults to item as text. |
Exposed Methods
| Method | Signature | Description |
|---|---|---|
scrollToIndex | (index: number) => void | Scrolls to Math.max(0, index) * itemHeight; indexes past the last item aren't clamped. |
scrollToTop | () => void | Scrolls to the top. |
scrollToBottom | () => void | Scrolls to max(0, totalHeight - viewHeight). |
Overview
- Every row is exactly
itemHeightpx tall; a numericheightbecomes px, and a string is applied as-is. - Numeric, px, and bare numeric string heights count immediately;
%,vh,vw,rem, andemwait for the mountedclientHeight, so give SSR or hidden containers a concrete height. - With
ResizeObserveravailable, container resizes update the viewport height. - The visible range runs from
floor(scrollTop / itemHeight) - overscanto the viewport's end plusoverscan, clamped between0anditems.length. - The spacer is
items.length * itemHeighttall, and visible items shift bystartIndex * itemHeight. - The container and rows carry no
roleoraria-*. For list semantics, wrap them withrole="list"/role="listitem"and setaria-setsize/aria-posinsetfrom the real total and absolute indexes.
Technologies
- Source:
packages/tuffex/packages/components/src/virtual-list/.
查看源码
packages/tuffex/packages/components/src/virtual-list/index.ts