Отображение большого количества элементов интерфейса является одной из распространённых проблем в веб-приложениях. Если компонент одновременно рендерит тысячи DOM-узлов, браузер начинает тратить значительные ресурсы на:
Даже современные браузеры испытывают сложности при работе с несколькими тысячами элементов одновременно. В результате появляются:
Особенно часто подобная ситуация возникает в следующих сценариях:
Для решения этой проблемы применяется виртуализация списков.
Виртуализация — это техника оптимизации интерфейса, при которой в DOM присутствуют только те элементы, которые находятся в области видимости пользователя.
Идея заключается в следующем:
Таким образом, вместо тысяч элементов браузер обрабатывает только несколько десятков.
| Подход | Количество DOM элементов | Производительность |
|---|---|---|
| Обычный список | 1000+ | низкая |
| Виртуализированный список | 20–50 | высокая |
Библиотека Element Plus содержит встроенные компоненты виртуализации, предназначенные для работы с большими наборами данных.
Основные из них:
Виртуализация реализована на уровне компонентов и не требует самостоятельной реализации алгоритма прокрутки.
Компоненты автоматически:
Компонент 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="items"
Тип:
Array
Каждый элемент массива передаётся в слот рендера.
Высота контейнера списка.
:height="400"
Если высота не указана, виртуализация работать не будет, так как компонент не сможет определить область отображения.
Высота одного элемента списка.
:item-size="50"
Значение должно соответствовать реальной высоте элемента.
Неверное значение приводит к:
Количество элементов, которые дополнительно рендерятся за пределами видимой области.
: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.
Алгоритм работы:
Пример:
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)
}
При работе с виртуализированными списками рекомендуется:
Нежелательно использовать сложную вложенность.
Хорошо:
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"
>
Классическая виртуализация предполагает фиксированный размер элементов.
Если высота динамическая, возможны проблемы с позиционированием.
Использование виртуализации оправдано только при больших объёмах данных.
Если список содержит:
виртуализация может быть избыточной и усложнять код.
50 000 пользователей
100 000 записей журнала
тысячи сообщений
десятки тысяч позиций
Внутренний алгоритм включает несколько этапов.
Определяется:
startIndex
endIndex
Дополнительно рендерятся элементы за пределами видимости.
visible + cache
Элементы смещаются с помощью:
transform: translateY()
или
top
При изменении scrollTop компонент:
Использование виртуализированных списков обеспечивает:
В интерфейсах с интенсивной обработкой данных виртуализация становится ключевым инструментом оптимизации клиентского рендеринга.