Как на JS реализовать «виртуальный список» (Virtual List) для отображения 100 000 строк без лагов в браузере?
Виртуальный список (Virtual List или Virtual Scroll) — это техника оптимизации, при которой в DOM отображается только небольшое количество элементов, видимых пользователю в данный момент, а не все 100 000 строк сразу. Это кардинально снижает нагрузку на браузер и устраняет лаги.
## Принцип работы
Идея проста: вместо того чтобы создавать 100 000 DOM-узлов, мы:
1. Создаём контейнер с фиксированной высотой и `overflow: auto`.
2. Внутри размещаем «распорку» (spacer) — элемент с высотой, равной суммарной высоте всех строк.
3. При прокрутке вычисляем, какие строки должны быть видны, и рендерим только их.
## Базовая реализация на чистом JS
html
javascript
const ITEM_HEIGHT = 40; // высота одной строки в px
const TOTAL_ITEMS = 100000;
const BUFFER = 5; // дополнительные строки сверху и снизу
const viewport = document.getElementById(‘viewport’);
const spacer = document.getElementById(‘spacer’);
const content = document.getElementById(‘content’);
// Устанавливаем полную высоту
spacer.style.height = ITEM_HEIGHT * TOTAL_ITEMS + ‘px’;
function getVisibleRange(scrollTop, viewportHeight) {
const start = Math.max(0, Math.floor(scrollTop / ITEM_HEIGHT) — BUFFER);
const end = Math.min(
TOTAL_ITEMS — 1,
Math.ceil((scrollTop + viewportHeight) / ITEM_HEIGHT) + BUFFER
);
return { start, end };
}
function renderItems(start, end) {
const fragment = document.createDocumentFragment();
for (let i = start; i <= end; i++) {
const div = document.createElement('div');
div.style.height = ITEM_HEIGHT + 'px';
div.style.position = 'absolute';
div.style.top = i * ITEM_HEIGHT + 'px';
div.style.width = '100%';
div.textContent = `Строка #${i + 1}`;
fragment.appendChild(div);
}
content.innerHTML = '';
content.appendChild(fragment);
}
function onScroll() {
const { start, end } = getVisibleRange(viewport.scrollTop, viewport.clientHeight);
renderItems(start, end);
}
viewport.addEventListener('scroll', onScroll);
onScroll(); // первоначальный рендер
## Оптимизации
— **requestAnimationFrame**: оберните вызов `renderItems` в `requestAnimationFrame`, чтобы синхронизировать рендер с частотой обновления экрана.
— **Переиспользование DOM-узлов (recycling)**: вместо удаления и создания элементов заново, обновляйте содержимое уже существующих узлов.
— **Throttle/Debounce**: ограничьте частоту вызовов обработчика прокрутки.
— **IntersectionObserver**: для более сложных сценариев.
— **Динамическая высота строк**: если строки имеют разную высоту, используйте массив накопленных смещений (offset cache).
## Готовые библиотеки
Если не хотите писать с нуля, используйте:
— **TanStack Virtual** (бывший react-virtual) — универсальное решение
— **vue-virtual-scroller** — для Vue.js
— **@angular/cdk/scrolling** — для Angular
— **react-window** / **react-virtualized** — для React
## Итог
Виртуальный список позволяет работать с огромными наборами данных, держа в DOM лишь 20–50 элементов одновременно. Это снижает потребление памяти в десятки раз и обеспечивает плавную прокрутку даже на слабых устройствах.
