Проблема производительности при работе с длинными списками При рендеринге длинных списков в веб-приложениях возникает критическая нагрузка на DOM. Если количество элементов превышает несколько сотен, браузер начинает заметно тормозить из-за большого количества одновременно отображаемых компонентов. Решением этой проблемы является виртуализация, когда на странице рендерятся только видимые элементы, а остальные создаются по мере прокрутки.
Использование компонента FixedSizeList из
react-window MUI не содержит встроенных
инструментов для виртуализации списков, но библиотека отлично
интегрируется с популярными решениями, такими как
react-window. Основной подход заключается в обёртывании
MUI-компонентов списка (List, ListItem) внутри
виртуализированного контейнера.
Пример базовой интеграции:
import React from 'react';
import { FixedSizeList } from 'react-window';
import List from '@mui/material/List';
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>
<FixedSizeList
height={600}
width="100%"
itemSize={46} // Высота одного элемента
itemCount={items.length}
>
{Row}
</FixedSizeList>
</List>
);
}
Ключевые моменты интеграции:
height и itemSize задают фиксированные
размеры контейнера и элементов. Они критичны для правильного вычисления
виртуального скролла.itemCount определяет общее количество элементов, чтобы
виртуализатор знал, сколько «мест» резервировать.style от
react-window, чтобы корректно позиционироваться в
контейнере.Использование VariableSizeList для элементов
разной высоты Если элементы списка имеют переменную высоту,
вместо FixedSizeList используется
VariableSizeList. В этом случае необходимо предоставлять
функцию, которая возвращает высоту элемента по индексу:
import { VariableSizeList } from 'react-window';
const getItemSize = index => index % 2 === 0 ? 50 : 70;
<VariableSizeList
height={600}
width="100%"
itemCount={items.length}
itemSize={getItemSize}
>
{Row}
</VariableSizeList>
Оптимизация рендеринга MUI-компонентов
MUI-компоненты, такие как ListItem и
ListItemText, часто содержат внутренние стили и эффекты,
что может замедлять виртуализированный список при большом количестве
элементов. Для оптимизации следует использовать:
React.memo для элементов списка, чтобы
избегать лишних перерисовок.Пример с мемоизацией:
const Row = React.memo(({ index, style }) => (
<ListItem style={style} key={index} divider>
<ListItemText primary={items[index]} />
</ListItem>
));
Объединение с MUI List и стилями Чтобы
сохранить стилизацию MUI, важно обернуть виртуализатор в компонент
List, но не рендерить каждый ListItem напрямую
в нём, иначе появятся конфликты с внутренней разметкой и padding.
Структура обычно такая:
<List>
<FixedSizeList>
{Row}
</FixedSizeList>
</List>
При этом padding и отступы List применяются ко всей
области, а не к отдельным элементам, что предотвращает визуальные
артефакты при скролле.
Ленивая подгрузка данных Виртуализация часто комбинируется с ленивой подгрузкой элементов. Можно отслеживать индекс последнего видимого элемента и подгружать новые данные при достижении конца списка:
const handleItemsRendered = ({ visibleStopIndex }) => {
if (visibleStopIndex >= items.length - 10) {
loadMoreItems(); // Функция загрузки новых элементов
}
};
<FixedSizeList
height={600}
width="100%"
itemCount={items.length}
itemSize={46}
onItemsRende red={handleItemsRendered}
>
{Row}
</FixedSizeList>
Выводы по использованию Виртуализация позволяет:
В сочетании с мемоизацией и ленивой подгрузкой виртуализация становится обязательным инструментом для производительных интерфейсов, где количество элементов списка может меняться динамически.