VirtualList
Fixed-row virtualized list for long data sets with lower DOM cost and imperative scroll helpers.
Usage
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 only for fixed-height rows. Variable-height content will desynchronize scroll math.
- Provide
itemKeyfor objects with stable ids; index keys are acceptable only for immutable arrays. - Keep
overscanmodest. Higher overscan smooths fast scrolling but increases DOM work. - Avoid wrapping rows with vertical margins; put padding inside the fixed-height row instead.
- Use
scrollfor analytics or lazy data triggers, not for per-frame heavy work.
API Reference
Props
| Prop | Type | Default | Description |
|---|---|---|---|
items | T[] | [] | Full data set. |
itemHeight | number | required | Fixed row height in pixels. All rows must match this value. |
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 | Key resolver for rendered rows. |
Events
| Event | Payload | Description |
|---|---|---|
scroll | { scrollTop: number, startIndex: number, endIndex: number } | Emitted after the list scrolls. |
Slots
| Slot | Props | Description |
|---|---|---|
item | { item: T, index: number } | Custom renderer for each visible item. Defaults to rendering item as text. |
Expose
| Method | Signature | Description |
|---|---|---|
scrollToIndex | (index: number) => void | Sets scrollTop to Math.max(0, index) * itemHeight. |
scrollToTop | () => void | Sets scrollTop to 0. |
scrollToBottom | () => void | Sets scrollTop to max(0, totalHeight - viewHeight). |
Overview
itemHeightis required and every rendered row is styled with that exact pixel height.- Numeric
heightbecomes px; stringheightis applied as-is. - Numeric, px, and bare numeric string heights are parsed immediately to compute the viewport.
%,vh,vw,rem, andemheights wait for the real container height. - On mount, the component reads
clientHeight; whenResizeObserveris available, it updates viewport height after resize. startIndexisfloor(scrollTop / itemHeight) - overscan, clamped to0.endIndexis based on visible viewport plusoverscan, clamped toitems.length.- The spacer height is
items.length * itemHeight; visible items are translated bystartIndex * itemHeight. itemKeymay be a field name or function. Missing field values fall back to the visible item index.scrollemits{ scrollTop, startIndex, endIndex }after internal scroll state updates.scrollToIndex(index)clamps negative indexes to zero but does not clamp indexes above the last item; callers should pass valid indexes.- The container and rows stay semantically neutral (no
role/aria-*). Because only the visible slice is in the DOM, consumers who need accessible list semantics should supplyrole="list"/role="listitem"plusaria-setsize/aria-posinsetthemselves, using the realitems.lengthand absolute indices rather than the visible-slice offsets.
Technologies
- Viewport note:
heightvalues that cannot be parsed synchronously (%, viewport units,rem,em) depend on the mounted element'sclientHeight; server-side or hidden containers should provide a concrete height before relying on visible range math. - Scroll note:
scrollToIndex()clamps negative indexes but not indexes aboveitems.length - 1. Validate caller indexes for user-provided jump targets. - Verified coverage:
virtual-list.test.tscurrently checks visible item count from fixed height and thescrollToIndexexposed method. The docs therefore call out untested contracts—ResizeObserver, customitemKey, overscan, andscrollpayloads—explicitly for manual review. - Component source:
packages/tuffex/packages/components/src/virtual-list/src/TxVirtualList.vue. - Types:
packages/tuffex/packages/components/src/virtual-list/src/types.tsexportsVirtualListProps,VirtualListEmits,VirtualListItemKey, andVirtualListKey. - Export alias:
packages/tuffex/packages/components/src/virtual-list/index.tsexportsVirtualList,TxVirtualList, virtual-list types, andTxVirtualListInstance. - Coverage:
packages/tuffex/packages/components/src/virtual-list/__tests__/virtual-list.test.tsverifies visible range rendering and imperative scrolling.
查看源码
packages/tuffex/packages/components/src/virtual-list/index.ts