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

Отображение большого количества элементов интерфейса является одной из распространённых проблем в веб-приложениях. Если компонент одновременно рендерит тысячи DOM-узлов, браузер начинает тратить значительные ресурсы на:

  • построение DOM-дерева;
  • вычисление стилей;
  • перерасчёт layout;
  • перерисовку интерфейса.

Даже современные браузеры испытывают сложности при работе с несколькими тысячами элементов одновременно. В результате появляются:

  • заметные задержки при прокрутке;
  • увеличение времени первоначальной загрузки;
  • рост потребления памяти;
  • зависания интерфейса.

Особенно часто подобная ситуация возникает в следующих сценариях:

  • длинные списки пользователей;
  • журналы событий;
  • списки сообщений;
  • каталоги товаров;
  • таблицы с большим количеством строк.

Для решения этой проблемы применяется виртуализация списков.


Принцип виртуализации

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

Идея заключается в следующем:

  1. Весь список логически существует в памяти приложения.
  2. В DOM рендерится только небольшой фрагмент элементов.
  3. При прокрутке элементы динамически заменяются новыми.
  4. Создаётся иллюзия присутствия полного списка.

Таким образом, вместо тысяч элементов браузер обрабатывает только несколько десятков.

Сравнение подходов

Подход Количество DOM элементов Производительность
Обычный список 1000+ низкая
Виртуализированный список 20–50 высокая

Поддержка виртуализации в Element Plus

Библиотека Element Plus содержит встроенные компоненты виртуализации, предназначенные для работы с большими наборами данных.

Основные из них:

  • Virtualized List
  • Virtualized Table
  • Virtualized Select

Виртуализация реализована на уровне компонентов и не требует самостоятельной реализации алгоритма прокрутки.

Компоненты автоматически:

  • вычисляют видимую область;
  • определяют необходимые элементы;
  • управляют позиционированием элементов в контейнере.

Компонент Virtualized List

Компонент ElVirtualList предназначен для отображения длинных вертикальных списков.

Основные особенности:

  • отображение только видимых элементов;
  • высокая производительность при тысячах записей;
  • гибкая настройка размеров элементов;
  • поддержка динамического обновления данных.

Базовое использование

Простейший пример виртуализированного списка.

<template>
  <el-virtual-list
    :data="items"
    :height="400"
    :item-size="50"
  >
    <template #default="{ item, index }">
      <div class="list-item">
        {{ index }} - {{ item }}
      </div>
    </template>
  </el-virtual-list>
</template>

<script setup>
import { ref } from 'vue'

const items = ref(
  Array.from({ length: 10000 }, (_, i) => `Элемент ${i}`)
)
</script>

Что происходит в этом примере

Создаётся массив из 10000 элементов, однако в DOM одновременно присутствует только небольшая часть списка.

Компонент рассчитывает:

  • текущую позицию прокрутки;
  • количество элементов, помещающихся в контейнер;
  • диапазон индексов для отображения.

При прокрутке элементы автоматически обновляются.


Основные свойства компонента

data

Источник данных списка.

:data="items"

Тип:

Array

Каждый элемент массива передаётся в слот рендера.


height

Высота контейнера списка.

:height="400"

Если высота не указана, виртуализация работать не будет, так как компонент не сможет определить область отображения.


item-size

Высота одного элемента списка.

:item-size="50"

Значение должно соответствовать реальной высоте элемента.

Неверное значение приводит к:

  • некорректной прокрутке;
  • визуальным смещениям элементов.

cache

Количество элементов, которые дополнительно рендерятся за пределами видимой области.

:cache="5"

Это позволяет сделать прокрутку более плавной.


Слоты рендера

Отображение элементов осуществляется через слот default.

<template #default="{ item, index }">

Доступные параметры:

параметр описание
item текущий элемент
index индекс элемента

Пример:

<template #default="{ item, index }">
  <div class="row">
    {{ index }} — {{ item.name }}
  </div>
</template>

Механизм позиционирования элементов

Виртуализированный список создаёт контейнер фиксированной высоты.

Высота рассчитывается как:

height списка = количество элементов × размер элемента

Однако в DOM создаётся только небольшой набор элементов.

Каждый элемент позиционируется через transform или top.

Пример структуры DOM:

container
  ├── spacer (общая высота списка)
  ├── item 1 (позиция 200px)
  ├── item 2 (позиция 250px)
  ├── item 3 (позиция 300px)

Таким образом сохраняется корректная прокрутка.


Генерация больших наборов данных

Для демонстрации виртуализации часто создают массивы большого размера.

const items = Array.from({ length: 50000 }, (_, index) => ({
  id: index,
  name: `User ${index}`
}))

Даже при 50 000 элементов интерфейс остаётся быстрым благодаря виртуализации.


Работа с динамическими данными

Виртуализированные списки корректно реагируют на изменения массива.

Пример добавления элементов:

items.value.push({
  id: 10001,
  name: 'New item'
})

После обновления данных компонент автоматически пересчитает размеры списка.


Обработка событий прокрутки

Для отслеживания прокрутки используется событие scroll.

<el-virtual-list
  :data="items"
  :height="400"
  :item-size="50"
  @scroll="handleScroll"
>
</el-virtual-list>

Обработчик:

function handleScroll({ scrollTop }) {
  console.log('Текущая позиция:', scrollTop)
}

Это полезно для:

  • подгрузки данных;
  • аналитики поведения пользователей;
  • синхронизации нескольких списков.

Подгрузка данных при прокрутке

Виртуализация часто используется совместно с lazy loading.

Алгоритм работы:

  1. список отображает текущие элементы;
  2. пользователь прокручивает вниз;
  3. приложение загружает новую порцию данных;
  4. список автоматически расширяется.

Пример:

function loadMore() {
  const start = items.value.length

  const newItems = Array.from({ length: 100 }, (_, i) => ({
    id: start + i,
    name: `Item ${start + i}`
  }))

  items.value.push(...newItems)
}

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

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

Минимизировать сложность DOM

Нежелательно использовать сложную вложенность.

Хорошо:

div
  span

Плохо:

div
  div
    div
      span
        strong

Использовать ключи

<div :key="item.id">

Это помогает Vue корректно отслеживать изменения.


Избегать тяжёлых вычислений

Внутри элемента списка не рекомендуется:

  • сложные вычисления;
  • синхронные запросы;
  • генерация больших объектов.

Виртуализированные таблицы

Для табличных данных в Element Plus используется компонент Virtualized Table.

Он предназначен для:

  • аналитических панелей;
  • логов;
  • административных интерфейсов.

Преимущества:

  • обработка десятков тысяч строк;
  • высокая скорость прокрутки;
  • поддержка сортировки и фильтрации.

Отличие от обычного списка

Особенность Обычный список Виртуализированный
DOM элементы все только видимые
производительность падает при 1000+ стабильная
память высокая низкая
прокрутка тяжёлая плавная

Типичные ошибки при использовании

Неверный размер элемента

Если item-size не соответствует реальной высоте элемента, возникают:

  • смещение элементов;
  • скачки прокрутки;
  • неправильный расчёт диапазона.

Отсутствие фиксированной высоты

Компонент должен знать размер контейнера.

Неверно:

<el-virtual-list :data="items">

Правильно:

<el-virtual-list
  :data="items"
  :height="400"
>

Изменение высоты элементов

Классическая виртуализация предполагает фиксированный размер элементов.

Если высота динамическая, возможны проблемы с позиционированием.


Когда виртуализация не требуется

Использование виртуализации оправдано только при больших объёмах данных.

Если список содержит:

  • 20 элементов
  • 50 элементов
  • 100 элементов

виртуализация может быть избыточной и усложнять код.


Практические сценарии использования

Список пользователей

50 000 пользователей

Лог событий

100 000 записей журнала

Чат

тысячи сообщений

Каталог товаров

десятки тысяч позиций

Архитектура виртуализированных списков

Внутренний алгоритм включает несколько этапов.

1. Вычисление диапазона

Определяется:

startIndex
endIndex

2. Создание буфера

Дополнительно рендерятся элементы за пределами видимости.

visible + cache

3. Позиционирование элементов

Элементы смещаются с помощью:

transform: translateY()

или

top

4. Перерисовка при прокрутке

При изменении scrollTop компонент:

  1. пересчитывает индексы;
  2. обновляет список отображаемых элементов.

Преимущества виртуализации

Использование виртуализированных списков обеспечивает:

  • стабильную производительность;
  • плавную прокрутку;
  • низкое потребление памяти;
  • возможность работы с огромными массивами данных.

В интерфейсах с интенсивной обработкой данных виртуализация становится ключевым инструментом оптимизации клиентского рендеринга.