Найти в Дзене
NeuroNest

Как добиться честных 60 FPS даже у примитивного списка - без слёз, костылей и шаманства

Окей, друзья. Вот казалось бы, обычный список. Ну просто список. Примитивный. Просто отобразить элементы. Казалось бы — да что может пойти не так? А оно, как назло, лагает. Прокрутка дёрганая, всё тормозит, будто рендерим не текст, а 3D-анимацию с RTX на картошке. Знакомо? А ведь задача тривиальна: показать 100–1000+ элементов и не сорваться в ад. И вот тут начинается пляска: кто-то лезет в оптимизацию рендера, кто-то в виртуализацию, а кто-то просто матерится и пишет "ну, тут тупо фреймворк тормозит, ничего не поделаешь". Но мы-то с тобой не такие. Мы копнём, чтобы выжать свои честные 60 fps. Не потому что надо, а потому что приятно, когда всё летает. 1. Понимание проблемы: FPS — это не магия, а физика Если у тебя при прокрутке списка FPS падает ниже 60 — это значит, что главный поток (UI thread) не успевает за кадр (а у тебя на каждый кадр всего-то ~16.6 мс). Проскочил мимо — список подёрнулся, кадр пропущен, ты злишься. Всё, что ты добавляешь в список — текст, иконки, изображения, а

Окей, друзья. Вот казалось бы, обычный список. Ну просто список. Примитивный. Просто отобразить элементы. Казалось бы — да что может пойти не так? А оно, как назло, лагает. Прокрутка дёрганая, всё тормозит, будто рендерим не текст, а 3D-анимацию с RTX на картошке. Знакомо?

А ведь задача тривиальна: показать 100–1000+ элементов и не сорваться в ад. И вот тут начинается пляска: кто-то лезет в оптимизацию рендера, кто-то в виртуализацию, а кто-то просто матерится и пишет "ну, тут тупо фреймворк тормозит, ничего не поделаешь". Но мы-то с тобой не такие. Мы копнём, чтобы выжать свои честные 60 fps. Не потому что надо, а потому что приятно, когда всё летает.

1. Понимание проблемы: FPS — это не магия, а физика

Если у тебя при прокрутке списка FPS падает ниже 60 — это значит, что главный поток (UI thread) не успевает за кадр (а у тебя на каждый кадр всего-то ~16.6 мс). Проскочил мимо — список подёрнулся, кадр пропущен, ты злишься.

Всё, что ты добавляешь в список — текст, иконки, изображения, анимации, даже отступы — это дополнительная нагрузка. И если ты всё рендеришь разом и на лету — готовься к просадкам.

2. Virtualization рулит. Остальное — понты

Если ты ещё не используешь виртуализацию — ну, прости, но ты живёшь как будто в 2010. Реальный список, у которого 1000 элементов и всё это одновременно в DOM/в дереве/на экране — это просто самоубийство.

Virtual scroll (виртуализация) — показывает только то, что реально видно пользователю. Остальное в спячке. Не рендерится. Не отрисовывается. Не грузит память. Классика жанра.

Есть встроенные решения:

  • React — react-window, react-virtualized
  • Vue — vue-virtual-scroller
  • Своё самописное решение на канвасе, если ты true задрот и не боишься крови.

Ставишь — и уже дышится легче.

3. Реюзай, не пересоздавай

Частая ошибка — при скролле заново пересоздавать каждый элемент. Типа "ну а чо, быстро же". Не быстро. Особенно если ты, допустим, на каждом элементе перезапрашиваешь данные, пересоздаёшь обработчики событий, пересчитываешь стили.

Решение простое, как хруст в шее: реюз компонентов. Не пересоздавай DOM, не пересоздавай объекты. Один раз отрисовал — дальше просто меняй данные.

Пример: если у тебя в списке карточки товаров — не надо каждый раз пересоздавать весь JSX/DOM, просто передай в реюзнутый компонент новый props.

4. Минимизируй пересчёты и перерасчёты

Тут уже чуть сложнее. Часто тормоза — это не сами элементы, а то, что они запускают кучу каскадных перерасчётов. Поменял высоту одного блока — и весь список заново вычисляет layout. Печально.

Решение:

  • Задай фиксированную высоту элементов, если можешь.
  • Используй CSS transform/translate вместо top/left — они не запускают layout-расчёты.
  • Минимизируй reflow (да, старое слово, но актуальное).
  • И, блин, не ставь box-shadow в каждый элемент — это же убийство GPU!

5. Изображения: самые коварные тормоза

Ты думаешь, картинка — это просто? А она — как мина. Поставил одну неаккуратно — и всё, у тебя уже вместо 60 fps — PowerPoint.

  • Не загружай full-res картинки на скролле. Используй placeholder'ы, lazy load, замены типа blur-up.
  • Используй сжатые форматы — WebP, AVIF, или хотя бы уменьшай jpg.
  • Отрисовывай картинки на фоне, если их не надо интерактивно крутить.
  • Кэшируй, мать его. Если ты каждую прокрутку грузишь аватарку из сети — ты не оптимизатор, ты диверсант.

6. Batch update или как не убить фрейм

Когда пользователь скроллит, не надо пушить в интерфейс каждое изменение по отдельности. Ты создаёшь очередь обновлений, которую UI-поток просто не успевает проглотить.

Вместо этого:

  • Группируй обновления.
  • Используй requestAnimationFrame.
  • В React — useTransition и useDeferredValue (если ты на последней версии).
  • В Vue — nextTick и батчинг реактивности.

Не надо кормить интерфейс по чайной ложке, накорми разом — и пусть работает.

7. Профилируй как бог

Если ты не включал профайлер, ты не знаешь, где у тебя узкое горлышко. Даже не начинай оптимизировать вслепую. Ты же не лечишь болезнь по интуиции, правда?

  • Chrome DevTools → Performance
  • React Profiler
  • Flipper для React Native
  • Firefox Profiler — кстати, тоже мощный

Посмотри, где залипает main thread. Что дольше всего жрёт кадры. Какие компоненты чаще всего пересоздаются. И только потом — оптимизируй.

8. Не забывай про feel

Ну и вишенка: 60 fps — это не только про технику, но и про впечатление. Иногда у тебя может быть 60 кадров, но ощущение всё равно "медленно". Потому что input-лаг, потому что "неприятная" анимация, потому что интерфейс ведёт себя неестественно.

Добавь лёгкий инерционный скролл, убери рывки, сгладь переходы. Пусть всё не просто летает, а порхает, как бабочка, а не шарахается, как трамвай.

-2

Итог такой: 60 fps для списка — это не роскошь, а минимальный стандарт UX. Не надо делать ракеты из простых списков, но и забивать на производительность тоже нельзя. Всё, что ты рендеришь — имеет цену. И если ты хочешь, чтобы твой интерфейс воспринимался "легким" — придется попотеть. Но оно того стоит.

А теперь вопрос тебе, дорогой друг: как ты решаешь проблему лагов в списках — латаешь последствия или копаешься в корне? Какой твой любимый приём оптимизации? Делись в комментах, не будь одиночкой в этом лаговом аду.