Skip to content

VirtualList ​

Основной компонент. Рендерит только строки, видимые во вьюпорте, плюс буфер overscan. ResizeObserver измеряет каждую строку после монтирования, поэтому строки переменной высоты обрабатываются автоматически.

Пропы ​

items ​

T[]

Массив данных.

keyField ​

string · по умолчанию: 'id'

Поле, используемое как :key для каждой строки.

estimatedItemSize ​

number | (item, index) => number · по умолчанию: 50

Начальная оценка размера строки — высота, либо ширина при horizontal.

overscan ​

number · по умолчанию: 3

Дополнительные строки, рендерящиеся над/под вьюпортом.

minHeight ​

number · по умолчанию: 0

Минимальная общая высота списка в px (вертикальный режим).

minWidth ​

number · по умолчанию: 0

Минимальная общая ширина списка в px (режим horizontal).

scrollElement ​

HTMLElement | null · по умолчанию: null

Внешний контейнер прокрутки (взаимоисключим с pageMode).

pageMode ​

boolean · по умолчанию: false

Использовать window как контейнер прокрутки. Фиксируется при монтировании — см. примечание ниже.

horizontal ​

boolean · по умолчанию: false

Виртуализация по scrollLeft/clientWidth вместо scrollTop/clientHeight. RTL-безопасно. Фиксируется при монтировании — см. примечание ниже.

pageMode и horizontal считываются один раз при монтировании — как выбор оси/режима, а не реактивный проп в реальном времени. Если ваш UI позволяет пользователям переключать horizontal во время выполнения, привяжите :key к значению, которое им управляет (например, :key="layout"), чтобы Vue перемонтировал компонент вместо того, чтобы оставить прежнюю ось подключённой — см. переключатель Layout на вкладке демо VirtualList для рабочего примера.

isLoading ​

boolean · по умолчанию: false

Показывает слот #skeleton, когда items пуст.

restoreKey ​

string · по умолчанию: —

Ключ для сохранения/восстановления позиции прокрутки в sessionStorage.

ssrPreloadCount ​

number · по умолчанию: 20

Число строк, рендерящихся на сервере.

recyclePool ​

boolean · по умолчанию: false

Переиспользовать DOM-узлы вместо их размонтирования (лучше FPS прокрутки, отключает переходы на основе ключа).

motionBlur ​

boolean · по умолчанию: false

Применить CSS-размытие, масштабируемое по скорости прокрутки, снимаемое ~150мс после остановки.

containerRole ​

string · по умолчанию: 'list'

ARIA-роль контейнера прокрутки. Оборачивающий компонент (например, VirtualTree или VirtualSelect) выставляет 'none', когда роль уже задана снаружи — тогда элемент убирается из дерева доступности вместо вложения одной роли в другую.

itemRole ​

string · по умолчанию: 'listitem'

ARIA-роль обёртки каждой строки. Установите 'none', если содержимое слота само рендерит реальную роль строки (например, treeitem, option) — иначе обёртка создаст невалидное вложенное дерево доступности.

Слоты ​

default ​

Область видимости: { item: T, index: number, style }

Содержимое строки.

empty ​

Область видимости: нет

Рендерится, когда items пуст и загрузки нет.

skeleton ​

Область видимости: нет

Рендерится, когда items пуст и isLoading истинно.

loading ​

Область видимости: нет

Рендерится внизу, пока isLoading истинно.

Emits ​

scroll ​

Payload: Event

Нативное событие прокрутки.

visible-range-change ​

Payload: { start: number; end: number }

Срабатывает при изменении видимого среза.

Публичный API (VirtualListExpose) ​

scrollTo(index, align?, options?) ​

Прокрутить к элементу. align — 'start' | 'center' | 'end' | 'auto'. options.behavior — 'auto' (по умолчанию, мгновенно) или 'smooth' (нативная анимация плавного скролла).

scrollToOffset(px, options?) ​

Прокрутить к пиксельному смещению. Тот же options.behavior.

measureItem(index, height) ​

Вручную задать высоту строки.

getScrollElement() ​

Возвращает элемент, который реально прокручивается — сочетайте с VirtualScrollbar.

ts
import type { VirtualListExpose } from 'vue-virtual-scroller-kit'

const listRef = ref<VirtualListExpose | null>(null)

listRef.value?.scrollTo(index, align) // 'start' | 'center' | 'end' | 'auto'
listRef.value?.scrollTo(index, 'start', { behavior: 'smooth' }) // анимация нативного плавного скролла
listRef.value?.scrollToOffset(px) // прокрутить к пиксельному смещению
listRef.value?.scrollToOffset(px, { behavior: 'smooth' })
listRef.value?.measureItem(index, height) // вручную задать высоту строки
listRef.value?.getScrollElement() // элемент, который реально прокручивается — сочетайте с VirtualScrollbar

behavior: 'smooth' по умолчанию 'auto' (мгновенный прыжок, как и раньше). Плавная прокрутка к далёкому виртуализированному индексу нацеливается на текущее оценочное смещение. Прямая запись scrollTop (например, из компенсации якоря, описанной ниже) иначе отменила бы выполняющийся нативный плавный скролл, поэтому эта компенсация подавляется ~1с после любого вызова с behavior: 'smooth'.

Примеры ​

Строки фиксированной высоты:

vue
<VirtualList :items="rows" :estimated-item-size="48" style="height: 500px">
  <template #default="{ item }">
    <div class="row">{{ item.name }}</div>
  </template>
</VirtualList>

Строки переменной высоты (оценка на элемент):

vue
<VirtualList
  :items="posts"
  :estimated-item-size="(item) => (item.isExpanded ? 200 : 60)"
  style="height: 600px"
>
  <template #default="{ item }">
    <PostCard :post="item" />
  </template>
</VirtualList>

Внешний контейнер прокрутки:

vue
<div ref="scrollEl" style="overflow-y: auto; height: 400px">
  <VirtualList :items="rows" :scroll-element="scrollEl" :estimated-item-size="48">
    <template #default="{ item }"><Row :data="item" /></template>
  </VirtualList>
</div>

Page-режим (прокручивается вся страница):

vue
<VirtualList :items="rows" page-mode :estimated-item-size="80">
  <template #default="{ item }"><Article :post="item" /></template>
</VirtualList>

Состояние skeleton-загрузки:

vue
<VirtualList :items="items" :is-loading="isLoading" :estimated-item-size="56" style="height: 500px">
  <template #default="{ item }"><Item :data="item" /></template>
  <template #skeleton>
    <SkeletonRow v-for="i in 10" :key="i" />
  </template>
  <template #empty>
    <div>No items found.</div>
  </template>
</VirtualList>

Восстановление позиции прокрутки:

vue
<VirtualList :items="rows" restore-key="my-list" :estimated-item-size="48" style="height: 500px">
  <template #default="{ item }"><Row :data="item" /></template>
</VirtualList>

Пул переиспользования DOM (тяжёлые строки с высоким FPS):

vue
<VirtualList :items="rows" recycle-pool :estimated-item-size="80" style="height: 500px">
  <template #default="{ item }"><HeavyRow :data="item" /></template>
</VirtualList>

Примечание: recyclePool переиспользует DOM-узлы, поэтому переходы Vue на основе ключей для отдельных элементов работать не будут. Используйте, когда производительность рендера важнее покадровых анимаций элементов.

Motion blur при быстрой прокрутке:

vue
<VirtualList :items="rows" motion-blur :estimated-item-size="48" style="height: 500px">
  <template #default="{ item }">
    <div class="row">{{ item.name }}</div>
  </template>
</VirtualList>

Горизонтальная раскладка (лента карточек):

vue
<VirtualList horizontal :items="cards" :estimated-item-size="220" style="height: 240px">
  <template #default="{ item }">
    <div class="card" style="width: 220px; height: 100%">{{ item.title }}</div>
  </template>
</VirtualList>

В горизонтальном режиме estimatedItemSize — это оценка ширины, и каждая строка позиционируется через inset-inline-start (RTL-безопасно) с height: 100% — содержимое слота само отвечает за собственную width (фиксированную либо измеряемую динамически через ResizeObserver, как и высоты строк в вертикальном режиме). Сочетайте с <VirtualScrollbar orientation="horizontal"> для темизируемого скроллбара.