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

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

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

MUI напрямую не предоставляет собственные компоненты для виртуализации, однако интеграция с библиотеками React Window и React Virtualized является стандартной практикой. MUI предоставляет стилизованные обертки, которые позволяют использовать свои компоненты List, ListItem и другие элементы с виртуализацией.

  • FixedSizeList (react-window) — отображает элементы фиксированной высоты.
  • VariableSizeList (react-window) — поддерживает элементы с различной высотой.
  • Grid (react-window) — позволяет виртуализировать сетки элементов.

Интеграция MUI с React Window

  1. Установка зависимостей:
npm install @mui/material react-window
  1. Базовый пример виртуализированного списка:
import * as React from 'react';
import { FixedSizeList as List } from 'react-window';
import ListItem from '@mui/material/ListItem';
import ListItemText from '@mui/material/ListItemText';

const items = Array.from({ length: 10000 }, (_, index) => `Элемент ${index + 1}`);

function VirtualizedList() {
  const Row = ({ index, style }) => (
    <ListItem style={style} key={index} divider>
      <ListItemText primary={items[index]} />
    </ListItem>
  );

  return (
    <List
      height={600}      // Высота контейнера списка
      itemCount={items.length}
      itemSize={50}     // Высота одного элемента
      width="100%"      // Ширина контейнера
    >
      {Row}
    </List>
  );
}

export default VirtualizedList;

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

  • height задает высоту видимой области списка.
  • itemCount — общее количество элементов.
  • itemSize — фиксированная высота каждого элемента.
  • style — обязательно передается каждому ListItem, чтобы корректно позиционировать элементы.

Поддержка переменной высоты элементов

Для элементов с разной высотой используется VariableSizeList:

import { VariableSizeList as List } from 'react-window';

const getItemSize = index => (index % 2 === 0 ? 60 : 40);

<List
  height={600}
  itemCount={items.length}
  itemSize={getItemSize}
  width="100%"
>
  {Row}
</List>

getItemSize возвращает высоту элемента по его индексу, что позволяет корректно отображать элементы разного размера.

Виртуализация сеток и таблиц

MUI Table можно комбинировать с виртуализацией, используя react-window или react-virtualized. Для таблиц с большим количеством строк рекомендуется использовать:

  • FixedSizeList для строк одинаковой высоты.
  • VariableSizeList для строк с переменной высотой.
  • Для столбцов с фиксированной шириной используется дополнительная логика для горизонтальной виртуализации.

Пример виртуализированной таблицы:

import Table from '@mui/material/Table';
import TableBody from '@mui/material/TableBody';
import TableCell from '@mui/material/TableCell';
import TableRow from '@mui/material/TableRow';
import { FixedSizeList as List } from 'react-window';

const rows = Array.from({ length: 10000 }, (_, index) => ({
  id: index,
  name: `Имя ${index + 1}`,
  value: Math.floor(Math.random() * 1000),
}));

const Row = ({ index, style }) => (
  <TableRow style={style} key={rows[index].id}>
    <TableCell>{rows[index].id}</TableCell>
    <TableCell>{rows[index].name}</TableCell>
    <TableCell>{rows[index].value}</TableCell>
  </TableRow>
);

<List
  height={600}
  itemCount={rows.length}
  itemSize={50}
  width="100%"
>
  {Row}
</List>

Ключевые моменты оптимизации

  • Передача style каждому элементу обязательна, иначе виртуализация сломается.
  • Использование key критично для корректной идентификации элементов при перерисовках.
  • Комбинация MUI и react-window позволяет сохранять фирменный стиль компонентов, не жертвуя производительностью.
  • Предварительный расчет высоты элементов улучшает плавность прокрутки при VariableSizeList.

Lazy Loading и виртуализация

Виртуализация можно комбинировать с ленивой подгрузкой данных. Вместо загрузки всех 10 000 элементов сразу, данные могут подгружаться порциями:

  1. Хранение текущего диапазона элементов в состоянии.
  2. Отслеживание прокрутки и загрузка следующей партии при достижении конца списка.
  3. Обновление itemCount и массива элементов для виртуализированного списка.

Эта стратегия снижает нагрузку на сеть и память, сохраняя отзывчивость интерфейса.

Взаимодействие с событиями

Виртуализированные списки корректно обрабатывают клики, фокус и навигацию клавиатурой, если:

  • Элементы получают уникальный key.
  • Стандартные MUI обработчики (onClick, onKeyDown) передаются внутрь Row.
  • Для сложных взаимодействий можно использовать контекст или хранилище состояния (Redux, Zustand).

Стилизация виртуализированных элементов

MUI полностью поддерживает свою систему стилей и темы:

  • Использование sx для стилизации каждого элемента:
<ListItem sx={{ bgcolor: index % 2 ? 'grey.100' : 'white' }}>
  <ListItemText primary={items[index]} />
</ListItem>
  • Стили из темы можно применять динамически, обеспечивая соответствие дизайну всего приложения.

Итоговая структура

Комбинация MUI с react-window предоставляет:

  • Высокую производительность при больших списках и таблицах.
  • Сохранение фирменного внешнего вида и компонентов.
  • Гибкость для фиксированных и переменных размеров элементов.
  • Возможность интеграции с ленивой подгрузкой и сложными событиями.

Эта техника является стандартом для создания масштабируемых интерфейсов с динамическими и объемными данными.