Frontend Engineer · Фев 2024 — Июль 2025

ОхотАктив · Производительность

Next.js · React · TypeScript · react-virtualized · Core Web Vitals · webpack-bundle-analyzer

Что это и с чем я пришёл

Крупный интернет-магазин товаров для охоты и рыбалки — до 10 млн просмотров в месяц, каталог до 400 тысяч товаров. Я пришёл незадолго до релиза React-версии сайта. Релизили в спешке: много кода писалось прямо в текучке, и переделать вёрстку до релиза глубоко было нельзя — сроки не позволяли. Так что стартовая точка была не «чистый лист», а живой продукт с уже заложенными проблемами производительности, которые нужно было чинить, не останавливая витрину.

Живой каталог: десятки фильтров с сотнями значений, сотни карточек — одна вёрстка на все устройства

Проблема

Lighthouse на каталоге показывал 40–50 из 100. Причина была в самом каталоге: десятки фильтров, у каждого — сотни значений, рядом карточки товаров, и всё это в одной адаптивной вёрстке сразу на десктоп и мобилку. На выходе получался очень тяжёлый HTML: браузеру приходилось строить и держать огромное дерево целиком. На ПК это лагало, на мобилке было совсем плохо — особенно когда пользователь открывал фильтры и в дерево добавлялись ещё сотни узлов.

Виртуализация фильтров

Первое, что я сделал — виртуализировал фильтры на клиенте готовой библиотекой (react-virtualized). Смысл простой: список на сотни значений не нужно рендерить целиком, если пользователь видит десяток строк за раз. Рендерится только видимая часть, остальное подставляется при скролле. Это разом сняло основную нагрузку с самого тяжёлого места страницы — дерево фильтров перестало раздувать DOM, и открытие фильтров, которое сильнее всего убивало мобилку, перестало лагать.

Отдача по User-Agent — и почему это не клоакинг

Дальше я развёл, что уходит поисковому боту и что — живому пользователю, по User-Agent. Боту нужно ровно то, что он индексирует: мета, семантика, все фильтры, весь значимый контент. Ему не нужен неинтерактивный обвес — обёртки, модалки и их стили с JS: он с ними всё равно не взаимодействует, а весят они прилично. Поэтому боту я этот обвес не отправлял, а пользователю уходила полная интерактивная версия.

Важная оговорка: это не streaming SSR и не клоакинг. Контент и семантика у бота и у пользователя одни и те же — бот видит ровно то же, что человек. Отличается только неинтерактивный обвес, которым боту нечего делать. Так поисковик получал чистую лёгкую страницу для индексации, а пользователь — весь интерактив, и никто из них не тянул лишнего.

Чистка первого рендера

Отдельно я вычистил первый экран. Всё, что не влияет ни на SEO, ни на первую отрисовку, ушло в динамические импорты и подгружалось позже: лишний CSS, JS и куски HTML не должны блокировать first paint. Задача была довести первый рендер до минимума — чтобы браузер как можно раньше показал пользователю значимое, а тяжёлое подъезжало уже после.

Почему фронт ускорил и сам сервер

Тут сыграла инфраструктура. Бэкенд был закэширован и быстр, а фронт и бэк стояли в одной сети на сервере — данные между ними летели практически мгновенно. Из-за этого узким местом ответа был именно фронт: пока Next.js собирал и отдавал тяжёлую страницу, уходило время. Как только страница похудела, время ответа Next.js резко упало — оптимизация фронта напрямую ускорила отдачу сервера.

Результат

40 → 100Lighthouse
−73%FCP
−63%LCP
1.5s → 0.25sОтвет Next.js

Lighthouse на каталоге поднялся с 40–50 до 100/100/100 — Performance, Accessibility, Best Practices. FCP упал на 73% (1.1s → 0.3s), LCP — на 63% (3s → 1.1s). Время ответа Next.js было ~1–1.5s с пиками до 3s, стало 0.25–0.30s почти на всех страницах (на тестовом стенде добивал до 0.16s). SEO-специалист отдельно отметил, что боты стали краулить сайт кратно быстрее — читаемость страниц для поисковика выросла вместе со скоростью.

Что это дало бизнесу

Ускорение потянуло за собой позиции в выдаче. Рейтинг сайта заметно вырос — почти догнали единственного конкурента, от которого раньше сильно отставали. Часть каталогов и страниц поднялась на много позиций и вышла на первую страницу поиска, некоторые заняли первое-второе место. В выдаче мы перебили многие страницы маркетплейсов — при их несопоставимо большем продвижении, бюджетах и узнаваемости.