Core Web Vitals каталога: LCP с 4,2 до 1,4 секунды
Замеры на каталоге из 9 200 позиций: что держало отрисовку, какие три решения сняли почти весь разрыв и 34 часа, не сдвинувшие ни одной метрики.
Клиент пришёл с сайтом на конструкторе, где четвёртый год лежало 640 позиций из 9 200 складских, и с просьбой «сделать зелёные цифры в PageSpeed». Мы начали не с цифр, а с замера в поле: LCP каталога на 4G — 4,2 с по медиане, а лаборатория на том же адресе показывала 2,3 с. Расхождение почти вдвое и есть первая интересная часть истории: два прибора мерили одну страницу и рассказывали про неё разное.
Дальше — что нашлось в этой разнице, какие три решения закрыли почти весь разрыв к запуску нового каталога и какие 34 часа мы потратили впустую.
Поле и лаборатория — два разных прибора
Оценку PageSpeed Insights приносят чаще всего, и она же меньше всего годится в качестве цели работ. Считается она по одному запуску на эмулированном устройстве, и одна и та же страница даёт разброс в 12—18 баллов между прогонами. Как отладочный инструмент — полезна. Как строка в договоре — нет: любое движение внутри разброса можно объявить и успехом, и провалом, причём не соврав ни в одном скриншоте.
Поэтому мы держим два источника и не смешиваем их.
Поле: CrUX и свой сбор в Метрику
Поле — это то, что видели настоящие посетители на своих телефонах и своём интернете. Отчёт CrUX плюс собственный сбор web-vitals в Метрику через параметры визита: так метрика видна в разрезе шаблонов, устройств и каналов, а не одним числом по домену. Поле обновляется с задержкой, поэтому мгновенного результата в нём не бывает никогда — первый честный замер после релиза мы делали через четыре недели.
Лаборатория: Lighthouse в CI
Лаборатория — Lighthouse в CI на каждый коммит, три прогона, берём медиану. Она нужна не для отчёта, а чтобы поймать регрессию в тот день, когда её внесли: кто-то вернул внешний шрифт, кто-то поставил виджет чата. Абсолютным числам лаборатории мы не верим.
Лаборатория показывала 2,3 с там, где у людей было 4,2 с. Поверь мы ей — работы бы просто не начались.
Пороги записываются до первой правки
Иначе порог потом подгоняется под результат. На этом проекте они были такие: LCP ≤ 2,5 с, INP ≤ 200 мс, CLS ≤ 0,1 у 75-го перцентиля. Оценки PageSpeed в списке нет вообще, и это тоже записано заранее.
Три слоя, которые держали отрисовку
Элемент LCP на карточке товара — главное изображение. Разбор цепочки загрузки показал три слоя проблемы, и только один из них был про картинку.
Адрес картинки знал только скрипт
Изображение подставляла галерея, которую инициализировал JavaScript. Браузер узнавал адрес главной картинки после того, как скачал и выполнил 340 КБ скриптов. Ни предзагрузка, ни приоритеты здесь не спасают: пока скрипт не отработал, грузить нечего — в разметке нет адреса.
Шрифт из внешнего сервиса
Шрифт подключался со стороннего домена: лишнее соединение, лишний обход DNS, блокировка отрисовки текста. На цепочке до первого кадра это давало 280 мс, которых не видно ни в одном отчёте по весу страницы — там всё выглядело прилично.
780 мс до первого байта
Сервер отдавал первый байт за 780 мс, и это была не вёрстка. Цена и остаток запрашивались у 1С синхронно при каждом рендере: страница ждала ответа учётной системы, а учётная система в это время считала отгрузки. Обмен с 1С — отдельная большая тема, и рвётся он в предсказуемых местах: где рвётся выгрузка из 1С.
Три решения, закрывшие почти весь разрыв
Все три сделаны в новом каталоге, который мы собрали вместо конструктора. Ни одно из них не про сжатие и минификацию: каждое убирает ожидание, а не байты.
Изображение переехало в разметку
Главная картинка карточки стоит в HTML статически — с размерами и явным приоритетом загрузки, без участия скрипта. Галерея по-прежнему на JavaScript, но она поднимается после того, как первый кадр показан. Скриптов на карточке в новой сборке 96 КБ против 340 КБ на старом сайте.
Шрифты стали своими
Шрифты уехали на свой домен и подключаются локально, на время загрузки текст показывается системным начертанием. Внешних соединений в критическом пути не осталось, и 280 мс ушли из цепочки вместе со сторонним сервисом.
Данные перестали ждать 1С
Синхронный запрос к учётной системе на рендере убран полностью. Остатки и цены по договору приходят обменом каждые 15 минут в свою базу, номенклатура — раз в сутки, страницы категорий рендерятся на сборке. Первый байт стал приходить за 210 мс вместо 780 мс, и — что важнее в нагруженный день — время ответа больше не зависит от того, сколько человек сейчас в каталоге и чем занята 1С.
Побочный эффект статической сборки касается адресов фильтров. Подбор по характеристикам умножает адреса: каждое сочетание — отдельная страница, которую надо и отрисовать, и решить, отдавать ли её поиску. Отрисовка тут бесплатная, потому что страницы собраны заранее, а вот что из этих адресов оставлять в индексе — разговор отдельный: дубли фильтров в каталоге.
Цифры до и после
Через четыре недели после запуска поле показало LCP каталога 1,4 с на 4G — это первая точка, которой можно верить. Остальные строки сняты позже, на общем замере через шесть месяцев.
| Метрика | До | После | Порог |
|---|---|---|---|
| LCP каталога, 4G | 4,2 с | 1,4 с | ≤ 2,5 с |
| INP на карточке | 140 мс | 120 мс | ≤ 200 мс |
| CLS | — | — | ≤ 0,1 |
| Отказы на странице поиска | 38% | 12% | — |
| Задержка остатков | 24 ч | 15 мин | — |
LCP и INP — поле, медиана визитов на 4G: так замер зафиксирован в приёмке проекта. Пороги заданы строже, по 75-му перцентилю, поэтому колонки нельзя вычитать друг из друга — медиана внутри порога ещё не значит, что перцентиль тоже внутри. CLS оставлен без цифр — сравнимого замера до работ у нас нет, а одно значение после сравнением не является. Отказы — Яндекс Метрика, страница поиска по каталогу. Задержка остатков — расписание обмена: была одна выгрузка в сутки ночью, стало каждые 15 минут. Замер 15.03.2026, через шесть месяцев после запуска.
Строку про отказы на поиске стоит читать осторожно: она упала с 38% до 12% в основном потому, что появился подбор по 12 характеристикам вместо поиска по названию. Скорость тут работает вместе с подбором, а не вместо него.
34 часа, не изменившие ни одной метрики
Самая полезная часть отчёта по скорости — список того, что не сработало. У нас его набралось на 34 часа, и мы этот счёт не прячем: он объясняет, почему сметы на скорость до замера не бывает.
14 часов на то, чего LCP не заметил
- Перевели все изображения каталога в AVIF. Выиграли 40 КБ на странице, LCP не сдвинулся: он ждал не байты, а адрес картинки в разметке.
- Настроили предзагрузку второстепенных ресурсов. На первый кадр это не влияет по определению, зато добавило конкуренции за канал.
- Вынесли критический CSS в разметку. Эффект был, пока шрифты жили на стороннем домене; после переноса шрифтов он пропал — обе правки лечили одно и то же место, и вторая оказалась не нужна.
20 часов на INP, который и так был в норме
INP на карточке был 140 мс при пороге 200 мс. Мы всё равно потратили на него двадцать часов и довели до 120 мс. Разницу не заметил никто: ни поле, ни клиент, ни поведение в Метрике. Это не значит, что INP не важен. Это значит, что метрику внутри порога бессмысленно улучшать, пока рядом стоит LCP в 4,2 с.
Почему это нельзя было посчитать заранее
Расклад обычный: три правки из десяти дают около 80% результата, но вычислить эти три без замера на живом сайте нельзя. Догадка про тяжёлые картинки на этом проекте была бы неверной трижды — отрисовку держали скрипт, сторонний шрифт и первый байт, и лишь один слой из трёх имел отношение к изображениям. Поэтому мы не берёмся за скорость по прайсу: сначала замер, потом смета.
Как это выглядело со стороны клиента
Скорость на этом проекте не была отдельной работой. Каталог собирали целиком: подбор по 12 характеристикам, обмен остатками, корзина под оптовые правила. Приписывать бизнес-результат одному LCP было бы нечестно, и мы этого не делаем.
За шесть месяцев после запуска заявки с сайта выросли с 11 до 26 в месяц, сборка одного заказа менеджером — с 14 минут до 4, доступных позиций стало 9 200 вместо 640. Главная причина здесь — подбор и живые остатки, а не отрисовка.
Прямое следствие скорости в этих цифрах видно одно: доля мобильных сессий длиннее 30 секунд выросла на 21%. Человек, которому каталог показался за 1,4 с, доходит до фильтра. Человек, который ждал 4,2 с, уходит раньше, чем увидит первый товар.
Гладко не было. Через полтора месяца после запуска выяснилось, что 1 400 позиций не находятся по характеристикам: наш собственный парсер молча отбрасывал номиналы, записанные дробью. Три недели часть каталога работала хуже старого сайта, потом пять дней ушло на исправление и два на переиндексацию. Это история про данные, а не про скорость, но яма от неё видна на графике заявок, и в разборе проекта она описана полностью, с датами.
Правило приёмки, которое из этого выросло
Вместо морали — четыре пункта, по которым мы теперь принимаем скорость каталога. Они написаны так, чтобы их мог проверить любой человек со стороны.
- Элемент LCP назван. В акте стоит не «сайт ускорен», а конкретный элемент конкретного шаблона и его время. Пока элемент не назван, обсуждать нечего.
- Замер в поле, через четыре недели после релиза. Лаборатория и оценка PageSpeed в приёмку не входят: только поле, только 75-й перцентиль, только порог, записанный до работ.
- Ни одна страница не обращается к учётной системе в момент рендера. Цена и остаток приходят обменом; при недоступной 1С каталог продолжает открываться.
- Часы без эффекта видны клиенту. Те 34 часа — строка в отчёте, а не тихо списанное время: иначе следующий проект начнётся с той же догадки про тяжёлые картинки.
Этот порядок приёмки входит в услугу магазин и каталог, и разбор номенклатуры в ней стоит первым этапом — раньше любой сметы на скорость. Потому что каталог, у которого цена и остаток приезжают из 1С в момент отрисовки, ускорить нельзя. Его можно только собрать заново.