Виртуализация списков

Веб-приложения с большими наборами данных часто сталкиваются с проблемой производительности при отображении длинных списков. Даже несколько сотен элементов в DOM могут заметно замедлить интерфейс. FAST Element предоставляет возможности для оптимизации через виртуализацию списков — стратегию рендеринга только видимой части элементов.


Основы виртуализации

Виртуализация заключается в том, что в DOM создаются только те элементы списка, которые реально видимы пользователю на экране. Элементы, выходящие за пределы видимой области, не рендерятся или удаляются из DOM, но при этом сохраняется корректная прокрутка.

Ключевые преимущества:

  • Снижение нагрузки на рендерер браузера.
  • Уменьшение объёма памяти, занятой DOM-узлами.
  • Поддержка плавной прокрутки даже при десятках тысяч элементов.

Компоненты FAST Element для виртуализации

В FAST Element основной механизм работы с виртуальными списками строится вокруг реактивных компонентов и директив:

  1. repeat — директива для итерации коллекций.
  2. when — условное отображение элементов.
  3. observable — реактивные свойства компонентов.

Простейший пример виртуализации с использованием repeat:

import { FASTElement, html, repeat } from "@microsoft/fast-element";

class VirtualList extends FASTElement {
  items = Array.from({ length: 10000 }, (_, i) => `Элемент ${i + 1}`);
}

VirtualList.template = html`
  <div style="height: 400px; overflow-y: auto;" @scroll="${x => x.onScroll()}">
    ${repeat(x => x.visibleItems, html`<div>${x => x}</div>`)}
  </div>
`;

VirtualList.prototype.onScr oll = function() {
  const scrollTop = this.$fastController.element.scrollTop;
  const itemHeight = 20;
  const viewportHeight = 400;

  const startIndex = Math.floor(scrollTop / itemHeight);
  const visibleCount = Math.ceil(viewportHeight / itemHeight);

  this.visibleItems = this.items.slice(startIndex, startIndex + visibleCount + 1);
};

В этом примере:

  • visibleItems содержит только элементы, которые должны отображаться.
  • Высота элементов фиксирована (itemHeight) для простоты вычислений.
  • Прокрутка инициирует пересчёт видимых элементов.

Оптимизация производительности

Для больших списков ключевыми аспектами являются:

  • Фиксированная высота элементов — упрощает вычисление индексов и позволяет легко управлять видимой областью.
  • Буферизация элементов — добавление нескольких невидимых элементов сверху и снизу списка для сглаживания прокрутки.
  • Делегирование событий — вместо назначения обработчика на каждый элемент лучше использовать один обработчик на контейнере.

Пример буферизации:

const buffer = 5; // добавляем по 5 элементов сверху и снизу
this.visibleItems = this.items.slice(
  Math.max(0, startIndex - buffer),
  Math.min(this.items.length, startIndex + visibleCount + buffer)
);

Продвинутая виртуализация

Для динамической высоты элементов применяются более сложные подходы:

  1. Измерение высоты каждого элемента и хранение карты позиций.
  2. Абсолютное позиционирование элементов внутри контейнера с полной высотой списка.
  3. Реактивное обновление видимых элементов при изменении размеров элементов или контейнера.

Пример структуры контейнера для переменной высоты:

<div style="height: 400px; overflow-y: auto; position: relative;" @scroll="${x => x.onScroll()}">
  <div style="height: ${x => x.totalHeight}px; position: relative;">
    ${repeat(x => x.visibleItems, html`
      <div style="position: absolute; top: ${x => x.itemPositions[x.index]}px;">
        ${x => x.item}
      </div>
    `)}
  </div>
</div>

Интеграция с реактивными данными

FAST Element позволяет использовать реактивные массивы и свойства для динамических списков. Обновление данных автоматически пересчитывает видимые элементы:

import { observable } from "@microsoft/fast-element";

class VirtualList extends FASTElement {
  @observable items = [];
  @observable visibleItems = [];
}

Использование @observable гарантирует, что любое изменение массива или его элементов вызовет реакцию компонента и обновит DOM.


Практические советы

  • Избегать сложных вложенных структур внутри виртуализируемых элементов, чтобы минимизировать затраты на рендеринг.
  • При возможном изменении высоты элементов использовать буфер и абсолютное позиционирование для сглаживания пересчетов.
  • Стараться хранить минимальный набор состояния, влияющего на рендеринг, чтобы реактивность не приводила к лишним обновлениям DOM.
  • Для списков с тысячами элементов обязательно использовать виртуализацию, иначе производительность падает пропорционально числу DOM-узлов.

Виртуализация списков в FAST Element сочетает в себе реактивность и лёгкость управления DOM, что позволяет создавать высокопроизводительные интерфейсы даже при огромных объёмах данных. Правильное сочетание repeat, буферизации и абсолютного позиционирования делает список отзывчивым и плавным при прокрутке.