|
Professor Seleznov
|
Довольно многие, кто близок к миру web разработки, не раз слышали про метрики web-vitals. По-настоящему значимыми они стали в середине 2021 года — в этот период Google официально стал учитывать метрики в ранжировании для мобильного поиска. С тех пор многие компании стремятся добиться хороших показателей метрик web-vitals. И, пожалуй, самая важная из этих метрик — LCP. Привет! Меня зовут Николай, я — frontend-разработчик в компании Иви. В зоне моей ответственности как раз и лежит web-версия онлайн-кинотеатра. И в четвертом квартале 2025 года команда web решила сосредоточить ресурсы на метрике LCP. Что такое web-vitals в целом и LCP в частности? Оглавление
Метрики web-vitals — что это? Web-vitals — это инициатива Google, которая выделяет ключевые метрики для оценки пользовательского опыта на сайте. Если раньше были десятки сложных показателей, то web-vitals фокусируется на нескольких, самых важных. Цель — дать единый, понятный язык для разработчиков, SEO-специалистов и руководителей, чтобы оценить, насколько быстро, отзывчиво и стабильно работает сайт для реальных пользователей.
Текущие ключевые метрики web-vitals
Ключевая тройка метрик меняется каждые несколько лет. Сейчас (2026) это:
- LCP (Largest Contentful Paint) — «Как быстро загружается основное содержимое?» (Метрика загрузки)
- INP (Interaction to Next Paint) — «Как быстро сайт реагирует на клики, тапы, нажатия клавиш?» (Метрика отзывчивости). (Примечание: заменил FID в 2024 году)
- CLS (Cumulative Layout Shift) — «Насколько стабильно отображается страница, не "прыгает" ли контент?» (Метрика визуальной стабильности)
Почему им уделяют столько внимания?
- Пользовательский опыт: Прямая связь с удовлетворенностью
- SEO: Являются ранжирующим фактором в поиске Google
- Бизнес-метрики: Хорошие web-vitals коррелируют с более высокой конверсией и вовлеченностью
Подробнее про метрику LCP Простыми словами: LCP измеряет, через какое время самый большой и заметный элемент в видимой области экрана (viewport) становится полностью отрисованным. Это момент, когда пользователь видит, что «страница уже грузится» и появляется главный контент. Что считается LCP-элементом?
- Изображения (<img>, <image> внутри <svg>)
- Постер-изображения внутри видео (<video> с poster)
- Элементы с фоновым изображением, загруженным через url() (css свойство background-image)
- Блочные элементы (<div>, <p>, <h1>) с текстовым узлом
Примеры с сайта Иви: для главной страницы LCP-элементом является постер в слайдере:
 для доступного контента LCP-элементом является постер в плеере:
 пример, где LCP-элементом является текстовый узел (заголовок страницы):
 Что НЕ считается LCP-элементом?
- Элементы, которые находятся за пределами видимой области
- Слишком мелкие элементы — выбирается всегда самый большой
- Анимированные или меняющиеся элементы
Пороги оценки LCP:
- Хорошо (Good): ≤ 2.5 секунды
- Требует улучшений (Needs Improvement): 2.5 – 4.0 секунды
- Плохо (Poor): > 4.0 секунды
Инструменты для мониторинга LCP Основным инструментом для мониторинга web-vitals метрик являются так называемые CrUX (Chrome User Experience) отчеты, которые строит Google. Эти отчеты находятся в открытом доступе и можно смотреть состояние метрик в принципе для любого сайта. Вот ссылка-пример такого отчета для сайта Иви: https://cruxvis.withgoogle.com/#/?view=loadingperf&url=https%3A%2F%2Fwww.ivi.ru%2F&identifier=origin&device=PHONE&periodStart=0&periodEnd=-1&display=p75s В качестве упражнения, попробуйте посмотреть CrUX отчеты ваших любимых сервисов.

Главная панель метрики LCP Тут можно обратить внимание на вкладку Controls — она позволяет гибко настраивать вывод графика метрик. Чем именно пользуется наша команда:
- В фильтре Data мы глобально ориентируемся на Origin — данное значение показывает LCP для всех страниц сайта в целом
- В фильтре Device — в первую очередь ориентируемся на мобильные устройства (Phone)
- И также нас интересует 75 перцентиль — это показатель, сколько страниц сайта имеют определенное значение метрики
Решение сосредоточить силы команды было принято исходя как раз из этого отчета, и основной нашей задачей было улучшить LCP для мобильных устройств, потому что значение метрики «подбиралось» к желтой зоне (>2.5 секунд). Почему также не десктоп? А для десктопа у нас значение метрики было с хорошим запасом (по данным CrUX отчетов в районе ~1800ms):
 И еще одной причиной внимания именно к мобильным значениям метрики является инициатива Google, называемая mobile-first-indexing. Это подход, при котором для сканирования, индексации и ранжирования сайтов используется в первую очередь их мобильная версия, а не десктопная. Это означает, что контент, структура и технические параметры (скорость, адаптивность), доступные на смартфонах, определяют место сайта в поисковой выдаче. Подробнее тут — https://developers.google.com/search/docs/crawling-indexing/mobile/mobile-sites-mobile-first-indexing?hl=ru
Препарируем метрику LCP — из чего она состоит? Для дальнейшего понимания и поиска узких мест надо разобраться, из чего же состоит метрика LCP и что именно показывают ее составляющие: Метрику LCP составляют четыре «подметрики» в порядке их подсчета при загрузке страницы с LCP-элементом:
- TTFB — Time to first byte — время от начала навигации до получения первого байта HTML-документа от сервера. Эта метрика показывает скорость работы бэкенда и сети. Важно отметить, что эта метрика также считается отдельной самостоятельной метрикой web-vitals, но при этом входит в конечный LCP
- Resource Load Delay — задержка начала загрузки LCP-элемента. Это время между TTFB и моментом, когда браузер фактически начал скачивание LCP-элемента. Большая задержка означает, что более важный ресурс ждал, пока скачаются менее важные
- Resource Load Duration — собственно время скачивания LCP-элемента. Тут все просто. На значение этой метрики влияет размер изображения, его степень сжатия, скорость работы сети и наличие кэша
- Element Render Delay — это время между окончанием загрузки элемента и его финальной отрисовки на экране. На эту метрику может влиять опять же размер изображения (от этого зависит скорость его декодирования на CPU, особенно для более слабых по сравнению с ПК мобильных устройств); ожидание основного потока (Main Thread); ожидание стилей
Очень удобно, что CrUX отчет также предоставляет графики этих составляющих LCP, где можно отдельно проанализировать изменение в течение времени каждой из них:

Анализ и выявление возможностей для оптимизации Сейчас по логике я должен рассказать, как мы проводили анализ и выявляли куда двигаться, но я сделаю небольшое отступление. Параллельно с тем, что команда взяла задачу по улучшению метрики, один из наших коллег решил сделать эксперимент. Исторически сложилось так, что картинки на сайте Иви в большинстве своем были в формате JPEG, тем временем как формат WebP уже обзавелся внушительной поддержкой современными браузерами. Было принято решение провести эксперимент с заменой формата с JPEG на WebP. Для замеров использовался стенд — пустая страница с одним вариантом картинки из двух: A) JPEG B) WebP Результаты отражены в таблице ниже:
| Перцентили LCP |
JPEG |
WebP |
Разница в процентах |
| p50 LCP (ms) |
1628 |
1284 |
-21.1% |
| p75 LCP (ms) |
1752 |
1344 |
-23.3% |
| p95 LCP (ms) |
2140 |
1396 |
-34.8% |
Увидев такие хорошие результаты, было принято решение уже на самом сайте Иви заменить формат картинок, являющихся LCP-элементом (см. примеры с сайта Иви выше): на Главной странице сайта и на карточках контента. По итогу эксперимента увидели улучшение метрики для Главной страницы на десктопной версии на 45ms, на мобильной версии — 35ms. На карточках изменений не было, но результат в целом нас устроил и открыл дорогу к дальнейшим поискам возможностей оптимизации LCP. Увы, не всегда достаточно просто внедрить новую технологию, чтобы улучшить метрики. Так что копнем глубже. Разобравшись с теоретической частью и вооружившись инструментами мониторинга, мы в первую очередь провели анализ LCP на сайте с большим вниманием к карточкам контента в контексте мобильной версии, так как карточки контента — это большинство страниц сайта Иви, и нам очень важна их скорость работы. С результатами замеров можно подробно ознакомиться в таблице ниже: Тестируемое окружение — прод сайта Иви, проблемный web-mobile, сетевой кэш отключен, средние результаты по 5 замерам LCP в ms «Легенда» таблицы:
- TTFB: Time To First Byte
- RLD: Resource Load Delay
- RLDura: Resource Load Duration
- ERD: Element Render Delay
- LCP Total: TTFB + RLD + RLDura + ERD
| Тип страницы |
Троттлинг |
TTFB |
RLD |
RLDura |
ERD |
LCP Total |
| Доступный к просмотру контент |
Нет |
384 |
26 |
108 |
315 |
832 |
| 4xCPU + Slow 4G |
381 |
343 |
2286 |
2100 |
5110 |
| 6xCPU + Slow 4G |
1268 |
265 |
1843 |
2153 |
5529 |
| 20xCPU + Slow 4G |
3060 |
365 |
5426 |
2205 |
11055 |
| 4xCPU + 3G |
453 |
1750 |
8339 |
6646 |
17188 |
| 6xCPU + 3G |
945 |
1784 |
9215 |
5663 |
17606 |
| 20xCPU + 3G |
3125 |
2301 |
7598 |
5120 |
18144 |
|
| Закрытый подпиской или покупкой контент |
Нет |
380 |
16 |
179 |
279 |
854 |
| 4xCPU + Slow 4G |
2509 |
354 |
3128 |
707 |
6698 |
| 6xCPU + Slow 4G |
4250 |
338 |
3095 |
781 |
8464 |
| 20xCPU + Slow 4G |
23129 |
512 |
2896 |
1187 |
27724 |
| 4xCPU + 3G |
1783 |
3023 |
11401 |
2156 |
18362 |
| 6xCPU + 3G |
293 |
4220 |
10047 |
5255 |
19814 |
| 20xCPU + 3G |
40548 |
1864 |
10425 |
3194 |
56030 |
|
| Так называемый «фейк» — карточки для SEO |
Нет |
1940 |
11 |
140 |
1616 |
3706 |
| 4xCPU + Slow 4G |
11054 |
403 |
1770 |
9995 |
23223 |
| 6xCPU + Slow 4G |
17154 |
413 |
1737 |
10283 |
29587 |
| 20xCPU + Slow 4G |
68414 |
740 |
1579 |
16769 |
87502 |
| 4xCPU + 3G |
16351 |
1937 |
6607 |
33641 |
58535 |
| 6xCPU + 3G |
26734 |
1718 |
6605 |
34438 |
69494 |
| 20xCPU + 3G |
97682 |
2141 |
6256 |
45391 |
151470 |
|
| Главная страница |
Нет |
2171 |
40 |
103 |
84 |
2400 |
| 4xCPU + Slow 4G |
1373 |
82 |
2745 |
2047 |
6257 |
| 6xCPU + Slow 4G |
902 |
160 |
5675 |
32 |
6768 |
| 20xCPU + Slow 4G |
854 |
206 |
5176 |
233 |
6469 |
| 4xCPU + 3G |
843 |
1507 |
14197 |
2283 |
18828 |
| 6xCPU + 3G |
844 |
1675 |
16557 |
482 |
19557 |
| 20xCPU + 3G |
997 |
1336 |
13656 |
3317 |
19304 |
Также для облегчения анализа были сделаны дополнительные задачи (не направленные на прямое улучшение LCP). Основной задачей стало добавление графика разбивки метрики LCP в Grafana. Этот график строится на основе отправляемых данных LCP в нашу аналитическую систему. Основное отличие от графиков в CruX отчетах заключается в том, что этот график обновляется в режиме реального времени, что обеспечивает команде веба непрерывный мониторинг и анализ метрики, и в случае негативных изменений позволяет оперативно реагировать на ухудшение. Как пример — несколько раз при проблемных релизах мы очень быстро выявляли проблему и откатывали релиз, не дожидаясь пересчета метрики со стороны Google. Скрин для понимания:
 Дополнительно был проведен ручной анализ DOM дерева на предмет оптимизаций LCP. По результатам анализа были сформулированы и реализованы следующие задачи (помимо упомянутого выше эксперимента с WebP):
- До начала работ по LCP на карточках контента LCP-элементом у нас являлись теги div с background-image. Гипотеза состояла в том, что если перевести LCP-элемент на тег image и обвесить его специальными атрибутами (fetchpriority = high и loading = eager), то можем получить некоторое улучшение LCP. Атрибут fetchpriority со значением high как бы говорит браузеру — этот ресурс надо загрузить как можно скорее, а атрибут loading со значением eager прямо указывает, что изображение должно загрузиться немедленно, в отличие от значения lazy. По итогам никакого улучшения не было выявлено, LCP остался в тех же значениях.
- Далее решили продолжать копать тему с тегом img и решили расширить этот тег тегом picture. Тут гипотеза состояла в том, что у нас что для десктопа, что для мобильных устройств отдавалась одна и та же по размерам картинка в довольно большом разрешении. Решили добавить респонсивные постеры с помощью тега picture. Целями опять стали LCP картинки на Главной и на карточках контента. По результатам получили небольшое улучшение LCP (~35-50ms) на всем сайте.
- Благодаря задаче с выводом составляющих LCP в графане заметили, что Element render Delay довольно высок, чтобы быть нормой. Решили копать в эту сторону дальше. Одной из предполагаемых причин было то, что у нас слишком загруженный Main Thread, но раскопки с использованием Perfomance в Chrome ни к чему не привели. Тогда решили зайти с другой стороны, не разбираться в кишках всего приложения и искать проблему там, а создать тестовый стенд и начать пошагово навешивать туда функционал, который в теории может замедлять Element Render Delay.
Возможно, тут возникнет вопрос — а было ли что-то по TTFB? Ответ — да, коллеги из нашей команды параллельно работали над улучшением кэширования для всего сайта. Вкратце, это ускорило TTFB и соответственно LCP по данным из аналитической системы. Впрочем, это тема для отдельной статьи, и возможно в будущем мы про это расскажем (увлекательная получится история, следите за обновлениями!).
Прицельно бьем в Element Render Delay Вследствие неудачи ресерча ERD, решили сделать стенд — начать с простого html файлика (без экосистемы React) с картинкой (которая будет считаться как LCP-элемент) и постепенно добавлять туда разный функционал, который в теории может ухудшать или улучшать время Element Render Delay. Условия эксперимента — mobile, без учета троттлинга процессора и сети, изначально используется картинка с нашего CDN; также создан вспомогательный скрипт в head, который находит LCP-элемент и вытаскивает из него Element render delay. Далее делаем 15 замеров (перезагрузок страницы) — и скрипт выведет средний ERD. Эксперименты и результаты собраны в табличке под катом (уж очень внушительной она оказалась):
Эксперименты и результаты
| Эксперимент/гипотеза |
Описание гипотезы |
Результат ERD по 15 замерам |
Вывод |
картинка в теге img в формате JEPG без атрибутов оптимизации |
посмотреть, как считается ERD, когда нет ничего в теории блокирующего ERD |
📊 Средний LCP: 284 мс ⏱️ Средний ERD: 28 мс 📈 Средний ERD%: 10,3% ✅ ОТЛИЧНО: Нормальный уровень ERD |
Без блокирующих в теории фич ERD в рамках нормы |
| тегу img добавлены атрибуты размеров |
возможно добавление таких атрибутов ускорит отрисовку картинки браузером |
📊 Средний LCP: 568 мс ⏱️ Средний ERD: 344 мс 📈 Средний ERD%: 19,3% ✅ ОТЛИЧНО: Нормальный уровень ERD |
Улучшения ERD не выявлено |
| картинка с другого источника, размеры те же |
предполагается, что есть проблемы с нашим CDN |
В пределах нормы |
По сравнению с другим источником наш CDN отдает картинку быстрее |
| конвертация JPEG в WebP |
проверить, как повлияет изменение формата на ERD |
📊 Средний LCP: 297 мс ⏱️ Средний ERD: 32 мс 📈 Средний ERD%: 12,0% ✅ ОТЛИЧНО: Нормальный уровень ERD |
На ERD изменение формата на WebP существенно не влияет |
| добавление атрибутов fetchpriority=high и loading=eager у тега img |
проверить, оказывают ли эти атрибуты влияние на ERD |
📊 Средний LCP: 281 мс ⏱️ Средний ERD: 33 мс 📈 Средний ERD%: 11,9% ✅ ОТЛИЧНО: Нормальный уровень ERD |
На ERD добавление атрибутов не оказывает эффекта |
| добавление в head медленных шрифтов |
медленные шрифты могут тормозить ERD |
📊 Средний LCP: 281 мс ⏱️ Средний ERD: 31 мс 📈 Средний ERD%: 11,6% ✅ ОТЛИЧНО: Нормальный уровень ERD |
Медленные шрифты не тормозят ERD |
| добавление "тяжелых" css вычислений в head |
тяжелые вычисления css могут тормозить ERD |
📊 Средний LCP: 280 мс ⏱️ Средний ERD: 29 мс 📈 Средний ERD%: 10,9% ✅ ОТЛИЧНО: Нормальный уровень ERD |
Также критичного ухудшения ERD не замечено |
| добавление тяжелого скрипта для создания long task |
тяжелые long task могут тормозить ERD |
ERD ухудшился |
long task ухудшают ERD примерно на время их выполнения |
| добавление микротасок |
Множество микротасок могут ухудшать ERD. Эксперимент запускался сначала с 60 микротасками, потом с 100, потом с 1000, потом без микротасок |
В пределах нормы |
Микротаски никак не влияют на ERD — на разных кол-вах микротасок изменений ERD нет вообще |
| добавление счетчиков GTM, Яндекс Метрики и тп как на сайте на КК |
выполнение скриптов аналитики может тормозить ERD |
В пределах нормы |
Счетчики аналитики никак не влияют на ERD |
| добавление бандла css |
проверить, оказывают ли влияние наши бандлы на ERD; Добавление в том же порядке, что и на кк (в head) |
В предалх нормы |
бандл css не влияет на ERD |
| добавление бандла js |
В пределах нормы |
внезапно, но бандл js + бандлы для его работы (react ecosystem, web cypher) не влияют на ERD |
| добавление бандла плеера css |
В пределах нормы |
На ERD css бандл плеера не влияет |
| добавление бандла плеера js |
В пределах нормы |
Тут влияния бандла на ERD также нет |
| Блок бандлов через Charles |
Посмотреть как влияет блокировка бандлов на ERD |
Негативные изменения ERD |
Если заблокирован только бандл css — сильно ухудшается ERD (на время блокировки примерно) Если заблокирован только бандл js — LCP в целом считается и отправляется по лого в шапке Если заблокированы оба бандла — LCP не считается ни в Perfomance tab, ни в расширении web-vitals хрома, не отправляется в грут после взаимодействия со страницей; отправляется кастомный LCP |
Зовем на подмогу — дополнительные эксперименты при помощи Charles Из всех экспериментов на стенде «удался» — то есть показал явное влияние на ERD — эксперимент с блокировкой загрузки основных бандлов через Charles.
Блокировка основного js бандла Подробнее опишу кейс, который был найден с помощью Charles. Мы обратили внимание на наши основные js и css бандлы. В них собраны скрипты и стили, которые нужны на всех разделах иви. Далее решили посмотреть, а что происходит, если загрузку этой статики «подвесить» в Charles, и увидели интересное поведение — при блокировке js бандла во вкладке Network у нас также подвисали два css файла для шапки сайта, а также для кнопки «Открыть» в приложении. Нюанс этих блоков в том, что их css лежит рядом с ними, а не в теге head. Это следствие реализации этих компонентов как Lazy-блоков. При этом эти блоки находились ДО блока, внутри которого весь основной контент страницы, включая LCP-элемент. Для понимания скрин DOM до оптимизации:
- Файл со стилями шапки
- Блок шапки, генерирующий css рядом с собой
- Стили кнопки Открыть в приложении
- Враппер, в котором лежит блок с LCP-элементом
 И при этом, до тех пор, пока эти два css файла не зарезолвились, мы наблюдали пустую страницу с фоном. Исходя из этого родилась задача — просто перенести эти блоки ниже блока с LCP-элементом, а стилями визуально сделать так, как будто переноса не было. Но к сожалению оказалось, что этот вариант оптимизации в отношении шапки сайта нам не подходит, так как в теории перенос шапки сильно сломает SEO. Пришлось откатить правки и подумать над другим решением. Обратили внимание, что именно Lazy рельсы компонента создают его css файл рядом. Поэтому решили сделать шапку обычным компонентом — стили шапки стали собираться в общем css бандле. Блок кнопки Открыть в приложении оставили после враппера. По итогу это почти простое решение дало очень мощный выхлоп — значительное улучшение метрики LCP на всем сайте в целом и на основных разделах в частности: ~200-250ms для Mobile, ~150-200ms для Desktop. Это подтвердилось как на наших внутренних графиках, так и на CrUX отчетах.
Блокировка основного css бандла Воодушевленные таким успехом, мы решили повторить эксперимент с подвисанием, только в этот раз с бандлом стилей. В этом кейсе все остальные скрипты и стили продолжали загружаться, но тем не менее, пока css бандл был в состоянии pending, на странице не было видно LCP-элемента. При снятии блокировки в charles LCP-элемент появлялся, а сама метрика, а конкретно ее составляющая Element Render Delay считалась примерно в ТО ЖЕ время, на которое была заблокирована загрузка css бандла. По результатам этого эксперимента родилась задача — сделать так, чтобы css бандл не блокировал рендер DOM дерева, а также добавить critical.css (или skeleton.css), который был бы легковесным и его скачивание занимало гораздо меньше времени, чем скачивание основного бандла. В рамках эксперимента сделали это только для карточек контента и только на мобильной версии сайта, под аб-тестом. По итогу зафиксировали значительное улучшение метрики LCP на mobile (~100-110ms), а конкретно составляющий LCP — Element Render Delay. Другие составляющие LCP без изменений (что, собственно, и ожидалось).
Из желтой зоны в зеленую — итоги наших оптимизаций До начала экспериментов по данным CrUX LCP для мобильных устройств подбирался к желтой зоне и даже в определенные временные промежутки попадал в нее, в пике значение было 2521ms. Но в итоге нашими экспериментами удалось снизить это значение на 419ms, до 2102ms по последним данным CrUX, в пересчете на проценты ~17%. Считаем это хорошим результатом как по абсолютным цифрам, так и по относительным. При этом стоит отметить, что мы пришли к выводу, что именно со стороны клиента возможности оптимизации мы исчерпали. Приведу рекомендации тем разработчикам, которые либо планируют, либо уже занимаются оптимизацией LCP:
- CrUX и его показатели — ваш основной ориентир
- Очень удобно настроить на своей стороне инструменты мониторинга — обязательно сделайте это!
- Нащупайте больные места вашей LCP, используя анализ подметрик — это покажет, в каких местах у вас проблемы. Для нас это стали показатели Element Render Delay и TTFB
- Следите за LCP-картинками — мир веб-разработки развивается стремительно, даруя новые возможности для оптимизации их загрузки
- Проанализируйте поведение ваших бандлов/скриптов при различных кейсах — велика вероятность их прямого влияния на LCP
На этом мой рассказ подходит к концу. Надеюсь, он был полезен для вас. Желаю вам удачи в ваших экспериментах!-Источник
|