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

Haunted — это библиотека для создания реактивных веб-компонентов на основе Web Components и Hooks, вдохновлённая подходом React, но использующая нативные возможности браузера. Одной из ключевых задач при работе с большими списками элементов является оптимизация их рендеринга. Виртуализация списков позволяет существенно повысить производительность, рендеря только видимую часть элементов, а не весь массив данных одновременно.


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

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

Основные компоненты подхода:

  • Контейнер списка: фиксированной высоты, с возможностью прокрутки.
  • Видимая область (viewport): элементы внутри этой области отображаются в DOM.
  • Буфер элементов: небольшое количество элементов перед и после видимой области, чтобы избежать визуальных срывов при скролле.
  • Подсчёт индексов: вычисление стартового и конечного индекса отображаемых элементов в зависимости от прокрутки.

Реализация в Haunted

Haunted не предоставляет встроенного компонента для виртуализации, но её можно реализовать с помощью хуков, реактивного состояния и наблюдения за прокруткой.

Пример использования useState и useEffect

import { html, component, useState, useEffect } from 'haunted';

function VirtualList({ items, itemHeight, viewportHeight }) {
  const [scrollTop, setScrollTop] = useState(0);

  const totalHeight = items.length * itemHeight;

  const startIndex = Math.floor(scrollTop / itemHeight);
  const endIndex = Math.min(items.length, Math.ceil((scrollTop + viewportHeight) / itemHeight));

  const visibleItems = items.slice(startIndex, endIndex);

  useEffect(() => {
    const onScr oll = (e) => setScrollTop(e.target.scrollTop);
    const container = document.getElementById('virtual-container');
    container.addEventListener('scroll', onScroll);
    return () => container.removeEventListener('scroll', onScroll);
  }, []);

  return html`
    <div id="virtual-container" style="height:${viewportHeight}px; overflow-y:auto; position:relative;">
      <div style="height:${totalHeight}px; position:relative;">
        ${visibleItems.map((item, i) => html`
          <div style="position:absolute; top:${(startIndex + i) * itemHeight}px; height:${itemHeight}px; width:100%;">
            ${item}
          </div>
        `)}
      </div>
    </div>
  `;
}

customElements.define('virtual-list', component(VirtualList));

Ключевые моменты:

  • Контейнер имеет фиксированную высоту и overflow-y: auto для прокрутки.
  • Высота внутреннего блока равна общей высоте всех элементов, чтобы скроллбар соответствовал длине списка.
  • Каждый элемент позиционируется абсолютно по вертикали на основе индекса.
  • scrollTop отслеживается через useState и обновляется с помощью обработчика события scroll.

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

  1. Буферизация элементов Для плавной прокрутки добавляется небольшой запас элементов сверху и снизу видимой области:
const buffer = 3; 
const startIndex = Math.max(0, Math.floor(scrollTop / itemHeight) - buffer);
const endIndex = Math.min(items.length, Math.ceil((scrollTop + viewportHeight) / itemHeight) + buffer);
  1. Функция рендеринга элементов Вместо инлайнового HTML можно использовать отдельную функцию для рендеринга элемента, чтобы уменьшить количество создаваемых шаблонов:
const renderItem = (item, index) => html`
  <div style="position:absolute; top:${index * itemHeight}px; height:${itemHeight}px; width:100%;">
    ${item}
  </div>
`;
  1. Throttle/Debounce событий scroll При очень длинных списках стоит ограничивать частоту обновления состояния scrollTop, используя requestAnimationFrame или debounce.

Особенности Haunted и виртуализации

  • Реактивность через хуки позволяет автоматически пересчитывать видимые элементы при изменении scrollTop или данных.
  • Изоляция стилей компонентов помогает избежать нежелательных перекрытий при абсолютном позиционировании.
  • Композиция с другими компонентами Haunted дает возможность встроить виртуализированный список в более сложные интерфейсы, сохраняя низкую нагрузку на DOM.

Выводы по архитектуре

Виртуализация в Haunted строится на сочетании реактивного состояния, позиционирования элементов и прослушивания событий прокрутки. Такой подход позволяет обрабатывать списки из тысяч элементов без существенного снижения производительности. Использование буфера элементов и оптимизация рендера с помощью функций обеспечивает плавный пользовательский опыт даже при интенсивной прокрутке.