Проблема производительности при работе с длинными списками
В веб-приложениях часто возникает необходимость отображать списки с тысячами элементов. Прямое рендерирование всех элементов одновременно приводит к значительным задержкам и нагрузке на DOM, что негативно сказывается на скорости интерфейса. Виртуализация решает эту проблему, рендеря только те элементы, которые видимы пользователю в данный момент, и динамически подгружая элементы при прокрутке.
В Carbon Components Svelte для этой цели можно использовать сочетание
стандартных Svelte-компонентов с библиотеками виртуализации или
собственными решениями на основе
<svelte:window> и
on:scroll.
Постраничная подгрузка (pagination) Простейший метод — загружать элементы частями (например, по 50–100 штук). Этот подход снижает нагрузку на DOM, но не обеспечивает плавной прокрутки, так как каждая новая порция данных требует асинхронного запроса или дополнительной обработки.
Виртуализация на основе viewport Элементы рендерятся только если они попадают в область видимости. При скролле старые элементы удаляются из DOM, новые добавляются. Это более сложный, но эффективный метод.
Структура данных и вычисление видимой области
<script>
import { onMount } from 'svelte';
let items = Array.from({ length: 10000 }, (_, i) => `Элемент ${i + 1}`);
let start = 0;
let end = 20; // изначально отображаем 20 элементов
let viewportHeight = 400;
let itemHeight = 40; // фиксированная высота одного элемента
let visibleItems = items.slice(start, end);
function handleScroll(event) {
const scrollTop = event.target.scrollTop;
start = Math.floor(scrollTop / itemHeight);
end = start + Math.ceil(viewportHeight / itemHeight);
visibleItems = items.slice(start, end);
}
</script>
<div on:scroll={handleScroll} style="height: {viewportHeight}px; overflow-y: auto;">
<div style="height: {items.length * itemHeight}px; position: relative;">
{#each visibleItems as item, i (item)}
<div style="position: absolute; top: {(start + i) * itemHeight}px; height: {itemHeight}px; width: 100%;">
{item}
</div>
{/each}
</div>
</div>
Ключевые моменты реализации:
overflow-y: auto
для создания scrollable зоны.start и end на основе
текущей позиции скролла.Компоненты Carbon, такие как DataTable, можно виртуализировать аналогичным способом. Основное отличие — обработка таблицы и строк через слоты и структуры компонентов Carbon.
Пример виртуализации DataTable:
<script>
import { DataTable, TableContainer, Table, TableHead, TableRow, TableHeader, TableBody, TableCell } from 'carbon-components-svelte';
let rows = Array.from({ length: 5000 }, (_, i) => ({ id: i + 1, name: `Элемент ${i + 1}` }));
let start = 0;
let end = 20;
let rowHeight = 50;
let viewportHeight = 400;
let visibleRows = rows.slice(start, end);
function handleScroll(event) {
const scrollTop = event.target.scrollTop;
start = Math.floor(scrollTop / rowHeight);
end = start + Math.ceil(viewportHeight / rowHeight);
visibleRows = rows.slice(start, end);
}
</script>
<TableContainer style="height: {viewportHeight}px; overflow-y: auto;" on:scroll={handleScroll}>
<Table>
<TableHead>
<TableRow>
<TableHeader>ID</TableHeader>
<TableHeader>Название</TableHeader>
</TableRow>
</TableHead>
<TableBody>
<div style="position: relative; height: {rows.length * rowHeight}px;">
{#each visibleRows as row, i (row.id)}
<TableRow style="position: absolute; top: {(start + i) * rowHeight}px;">
<TableCell>{row.id}</TableCell>
<TableCell>{row.name}</TableCell>
</TableRow>
{/each}
</div>
</TableBody>
</Table>
</TableContainer>
Особенности:
Буферизация элементов Чтобы избежать «мерцания» при быстрых прокрутках, добавляют небольшой буфер элементов до и после видимой зоны:
const buffer = 5;
visibleItems = items.slice(Math.max(0, start - buffer), Math.min(items.length, end + buffer));Динамическая высота элементов Если элементы имеют разную высоту, нужно хранить массив накопленной высоты и искать видимые элементы бинарным поиском, что сложнее, но обеспечивает корректную прокрутку.
Использование готовых библиотек виртуализации
on:resize)
позволяет корректно пересчитывать количество отображаемых
элементов.Виртуализация длинных списков в Carbon Components Svelte сочетает возможности Svelte для реактивного рендеринга с эффективной оптимизацией DOM, что обеспечивает высокую производительность и плавность интерфейса даже при работе с десятками тысяч элементов.