Перейти до вмісту
tatetUI

VirtualList

Віртуалізований список із фіксованою висотою рядка — рендерить лише видиме вікно діапазону з overscan-запасом.

Приклад

Рядок №1#0 · 0
Рядок №2#1 · 919
Рядок №3#2 · 838
Рядок №4#3 · 757
Рядок №5#4 · 676
Рядок №6#5 · 595
Рядок №7#6 · 514
Рядок №8#7 · 433
Рядок №9#8 · 352
Рядок №10#9 · 271
Рядок №11#10 · 190
Рядок №12#11 · 109
Рядок №13#12 · 28
Рядок №14#13 · 947
Рядок №15#14 · 866
Рядок №16#15 · 785
Рядок №17#16 · 704
Рядок №18#17 · 623
Рядок №19#18 · 542
Рядок №20#19 · 461
Рядок №21#20 · 380
Рядок №22#21 · 299
Рядок №23#22 · 218

Всередині сцени — 10 000 рядків; у DOM їх менше кількох десятків: списком керує позиція скролу, а не розмір масиву.

API

Props

НазваТипТиповоОпис
items*T[]Масив рядків. Порядок у масиві — порядок на екрані, компонент його не міняє.
itemHeight*numberВисота КОЖНОГО рядка в пікселях (число, не рядок). Це контракт компонента, а не налаштування: уся арифметика віртуалізації — позиція діапазону, зміщення рядків, загальна висота полотна — обчислюється множенням на це число. Рядки іншої висоти ламають розрахунок: останні екрани списку або не показуються, або показуються з дірками. Рядок має гарантовано вкладатися у задану висоту (`truncate`, `line-clamp-*`, фіксована кількість рядків тексту).
heightstring"20rem"Висота контейнера прокрутки, напр. `"16rem"`.
overscannumber5Скільки рядків за межами видимої зони тримати відрендереними з кожного боку. Більший overscan — плавніший скрол, більший DOM.
keyFieldstring"id"Поле-ідентифікатор рядка для `:key`.
ariaLabelstring"Віртуальний список"Доступна назва scrollable-списку.

Слоти

НазваОпис
itemОбов'язковий рендер одного рядка. Висоту рядку задає сам компонент.

Доступно через ref

НазваТипОпис
scrollToIndex(index: number) => voidПрокрутити до рядка за номером у `items` (центр вікна).

Слоти

Єдиний слот — item, і він обов'язковий: без нього списку нема чим малювати. Слот отримує { item, index } (index — абсолютний номер у items, а не позиція у вікні) і рендерить вміст ОДНОГО рядка. Висоту рядка задає сам компонент (itemHeight), тож у слоті не можна покладатися на min-h чи py- — рядок має вкладатися у відведені пікселі:

<UiVirtualList ref="list" :items="items" :item-height="36">
  <template #item="{ item, index }">
    <div class="flex items-center px-3">{{ index }}. {{ item.label }}</div>
  </template>
</UiVirtualList>

Програмна прокрутка — list.value.scrollToIndex(index): рядок опиняється посередині вікна, позиція клаймиться в межі полотна. При різкому скороченні items стара позиція скролу, що вилізла за нове полотно, скидається на початок — інакше вікно діапазону рахувалося б із «висячого» scrollTop і список показував би порожнє місце.

Коли використовувати

Для довгих однорідних списків — логів, стрічок платіжок, результатів пошуку, — де рендер всіх рядків означає тисячі зайвих вузлів DOM. Разом з UiPagination: пагінація зменшує обсяг даних, віртуалізація — кількість відрендерених вузлів.

Коли НЕ використовувати

Контракт фіксованої висоти. itemHeight — не налаштування, а передумова: уся арифметика віртуалізації (позиція діапазону, зміщення рядків, загальна висота полотна) обчислюється множенням на це число. Змінна висота рядків ламає розрахунок — останні екрани списку або не показуються, або показуються з дірками. Рядки різної висоти — це не «складніший випадок», а інший компонент.

Для коротких списків до кількох сотень рядків: звичайний v-for дешевший за вимірювання скролу, а браузер сам впорається з кількома сотнями вузлів.

Доступність

Прокрутка контейнера доступна з клавіатури як звичайна зона скролу (overflow-y-auto). Рядки — абсолютні прямокутники з координатами top = index * itemHeight, тож порядок у DOM відповідає порядку на екрані; ключ рядків береться з keyField, щоб фокус і стан рядка не загубилися під час перемальовування вікна.