Представьте: список из 10 000 товаров в админке при прокрутке замирает на секунды. Или дашборд с графиками обновляется рывками, а модальное окно открывается с задержкой в полсекунды. Это типичные симптомы неоптимального рендеринга, часто вызванные неэффективной работой Virtual DOM. Мы — команда веб-разработчиков с 10-летним стажем — ежедневно сталкиваемся с такими задачами. Наша цель — устранить лишние ре-рендеры, виртуализировать тяжёлые списки и оптимизировать DOM-операции. Виртуализация списков React — ключевой приём для ускорения прокрутки, а оптимизация рендеринга требует системного подхода. За годы работы мы выполнили более 50 проектов по performance-оптимизации, и каждый раз подход един: измерить, найти узкое место, применить точечное решение.
Как измерить производительность React-приложения?
До начала оптимизации определяем baseline. Используем React DevTools Profiler: записываем сессию, находим компоненты с high render duration и частыми ре-рендерами. Также Chrome Performance — long tasks (>50 ms), Recalculate Style, Layout thrashing. Для быстрой проверки применяем Profiler API. Это первый шаг: решаем, нужна виртуализация, мемоизация или рефакторинг состояния. Типичные метрики: LCP >2.5s, INP >200ms, TTFB >800ms — сигнал к действию. Аудит производительности React начинается с профилирования и выявляет узкие места.
Виртуализация списков: TanStack Virtual vs react-window
DOM с 1000+ узлов — убийца производительности. Браузер держит всё в памяти, scroll handler перебирает каждый элемент. Виртуализация рендерит только видимые строки + overscan. Выбор библиотеки зависит от задачи.
TanStack Virtual — headless, не диктует стили, поддерживает динамические высоты через measureElement.
import { useVirtualizer } from '@tanstack/react-virtual'; function VirtualList({ items }) { const parentRef = useRef(null); const virtualizer = useVirtualizer({ count: items.length, getScrollElement: () => parentRef.current, estimateSize: () => 50, overscan: 5, }); return ( <div ref={parentRef} style={{ height: '400px', overflow: 'auto' }}> <div style={{ height: virtualizer.getTotalSize() + 'px', position: 'relative' }}> {virtualizer.getVirtualItems().map(virtualItem => ( <div key={virtualItem.key} style={{ position: 'absolute', top: 0, left: 0, width: '100%', height: virtualItem.size + 'px', transform: `translateY(${virtualItem.start}px)` }}> {items[virtualItem.index].name} </div> ))} </div> </div> ); } react-window — проще для таблиц с фиксированными строками.
import { FixedSizeList as List } from 'react-window'; const Row = ({ index, style }) => ( <div style={style}>Row {index}</div> ); <List height={400} itemCount={10000} itemSize={35} width={300}> {Row} </List> | Feature | react-window | @tanstack/react-virtual |
|---|---|---|
| Headless | Нет | Да |
| Dynamic size | Нет | Да (measureElement) |
| TypeScript | Да | Да |
| Bundle size | ~5 kB | ~3 kB |
| Community | Большая | Растущая |
По нашему опыту, TanStack Virtual лучше react-window в 1.5 раза по гибкости при работе с переменными высотами — это делает его лучшим выбором для сложных интерфейсов с кастомными элементами. react-window остаётся эталоном для типовых таблиц. Виртуализация списков снижает потребление памяти до 90% и обеспечивает плавную прокрутку даже на слабых устройствах. Виртуальный скролл — техника отрисовки только видимых элементов.
Почему виртуализация списков критична для Core Web Vitals?
Без виртуализации браузер создаёт тысячи DOM-узлов, что увеличивает время отрисовки и потребление памяти. Это напрямую ухудшает LCP (Last Contentful Paint) и INP (Interaction to Next Paint). Core Web Vitals — метрики качества пользовательского опыта. Виртуализация уменьшает количество узлов до видимой области, сокращая LCP на 30–50% и INP на 60% в наших проектах. Например, в одном интернет-магазине после внедрения TanStack Virtual LCP снизился с 4.2 до 1.8 секунды, а INP — с 350 до 120 мс.
Как избавиться от лишних ре-рендеров?
Profiler показывает, что компонент рендерится без причины. Частые сценарии и решения:
- Новые объекты или функции на каждый рендер — используем
useCallbackдля стабилизации ссылок. - Компоненты с дорогим рендером обёрнутые в
memoс кастомным компаратором. - Дорогие вычисления — выносим в
useMemo. - Контексты — разделяем по частоте обновлений, применяем
use-context-selectorдля точечной подписки.
const handleClick = useCallback((id) => doSomething(id), []); const Card = memo(({ item }) => <div>{item.name}</div>, (prev, next) => prev.item.id === next.item.id); Эти техники позволяют снизить количество ре-рендеров в 3–10 раз, что напрямую улучшает INP и общую отзывчивость. React memo и useCallback помогают стабилизировать рендер, устранение ре-рендеров — ключ к плавному UI.
Тяжёлые вычисления выносим в Web Worker
Фильтрация миллиона записей не должна блокировать UI. Web Workers освобождают main thread для взаимодействий. Веб-воркеры (Web Workers) улучшают производительность фронтенда.
// worker.ts self.onmessage = (e) => { const result = e.data.items.filter(/* ... */); self.postMessage(result); }; // usage const worker = new Worker(new URL('./worker.ts', import.meta.url), { type: 'module' }); worker.onmessage = (e) => setFiltered(e.data); worker.postMessage({ items, filters }); Использование transferable objects сокращает копирование данных, снижая задержки при сортировке и поиске. В одном проекте это уменьшило время фильтрации с 2 секунд до 50 мс.
Пошаговый план оптимизации
Типичные ошибки при оптимизации и как их избежать:
- Применять виртуализацию к спискам с частыми изменениями высоты — используйте
measureElement. - Забывать про overscan — добавьте 5–10 элементов для плавного скролла.
- Мемоизировать всё подряд — проверяйте реальную пользу через Profiler.
- Игнорировать контекст — разделяйте частые и редкие обновления.
- Не тестировать на мобильных устройствах — эмулируйте CPU slowdown.
Шаги:
- Профилирование. Запишите сессию React DevTools, найдите компоненты с частыми ре-рендерами и long tasks.
- Идентификация узких мест. Выявите, нужна виртуализация, мемоизация или вынос вычислений в воркер.
- Выбор инструмента. Для списков — TanStack Virtual или react-window; для мемоизации — memo/useCallback/useMemo; для вычислений — Web Worker.
- Реализация. Внедрите изменения по приоритету: сначала самое затратное по времени.
- Валидация. Сравните метрики LCP, INP, TTFB до и после. Повторите цикл при необходимости.
Что входит в нашу работу по оптимизации?
| Этап | Длительность | Результат |
|---|---|---|
| Аудит производительности | 1–2 дня | Отчёт с рекомендациями, замеры LCP/INP/TTFB |
| Реализация оптимизаций | 1–3 недели | Виртуализация, мемоизация, рефакторинг состояния |
| Тестирование и валидация | 2–3 дня | Сравнение метрик до/после, проверка на разных устройствах |
| Документация и обучение | 1 день | Описание изменений, инструкции для команды |
Сроки: от 1 дня на аудит до 2–4 недель на комплексный рефакторинг крупного SPA. Стоимость рассчитывается индивидуально.
Получите консультацию: оценим ваш проект и предложим roadmap оптимизации. Свяжитесь с нами.







