Components/VirtualList

VirtualList

Fixed-row virtualized list for long data sets with lower DOM cost and imperative scroll helpers.

VerifiedSince 0.3.4

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 itemKey for objects with stable ids; index keys are acceptable only for immutable arrays.
  • Keep overscan modest. Higher overscan smooths fast scrolling but increases DOM work.
  • Avoid wrapping rows with vertical margins; put padding inside the fixed-height row instead.
  • Use scroll for analytics or lazy data triggers, not for per-frame heavy work.

API Reference

Props

PropTypeDefaultDescription
itemsT[][]Full data set.
itemHeightnumberrequiredFixed row height in pixels. All rows must match this value.
heightnumber | string320Scroll container height. Numbers become px.
overscannumber4Extra rows rendered before and after the viewport.
itemKeykeyof T | (item: T, index: number) => string | numberindexKey resolver for rendered rows.

Events

EventPayloadDescription
scroll{ scrollTop: number, startIndex: number, endIndex: number }Emitted after the list scrolls.

Slots

SlotPropsDescription
item{ item: T, index: number }Custom renderer for each visible item. Defaults to rendering item as text.

Expose

MethodSignatureDescription
scrollToIndex(index: number) => voidSets scrollTop to Math.max(0, index) * itemHeight.
scrollToTop() => voidSets scrollTop to 0.
scrollToBottom() => voidSets scrollTop to max(0, totalHeight - viewHeight).

Overview

  • itemHeight is required and every rendered row is styled with that exact pixel height.
  • Numeric height becomes px; string height is applied as-is.
  • Numeric, px, and bare numeric string heights are parsed immediately to compute the viewport. %, vh, vw, rem, and em heights wait for the real container height.
  • On mount, the component reads clientHeight; when ResizeObserver is available, it updates viewport height after resize.
  • startIndex is floor(scrollTop / itemHeight) - overscan, clamped to 0.
  • endIndex is based on visible viewport plus overscan, clamped to items.length.
  • The spacer height is items.length * itemHeight; visible items are translated by startIndex * itemHeight.
  • itemKey may be a field name or function. Missing field values fall back to the visible item index.
  • scroll emits { 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 supply role="list"/role="listitem" plus aria-setsize/aria-posinset themselves, using the real items.length and absolute indices rather than the visible-slice offsets.

Technologies

  • Viewport note: height values that cannot be parsed synchronously (%, viewport units, rem, em) depend on the mounted element's clientHeight; 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 above items.length - 1. Validate caller indexes for user-provided jump targets.
  • Verified coverage: virtual-list.test.ts currently checks visible item count from fixed height and the scrollToIndex exposed method. The docs therefore call out untested contracts—ResizeObserver, custom itemKey, overscan, and scroll payloads—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.ts exports VirtualListProps, VirtualListEmits, VirtualListItemKey, and VirtualListKey.
  • Export alias: packages/tuffex/packages/components/src/virtual-list/index.ts exports VirtualList, TxVirtualList, virtual-list types, and TxVirtualListInstance.
  • Coverage: packages/tuffex/packages/components/src/virtual-list/__tests__/virtual-list.test.ts verifies visible range rendering and imperative scrolling.
查看源码
packages/tuffex/packages/components/src/virtual-list/index.ts