awesome-everything EN
↑ Обратно к восхождению

Браузер и фронтенд-рантайм

Трейдоффы метрик, RUM-атрибуция и цикл CI+поле

Суть Три vital разделяют бюджет производительности — фикс одной может сломать другую. Полная production-observability требует web-vitals RUM для field-вердикта и throttled CI-гейта для регрессий — ни одного в отдельности недостаточно.
Высота — путь к senior
НольJuniorMiddleSenior
Ты на senior-высоте — в орбите
◷ 16 min

Команда фиксит LCP инлайнингом полного блока критического CSS — render delay LCP падает на 400 мс. Но TTFB растёт на 60 мс из-за того, что HTML теперь на 40 КБ больше. Другая команда lazy-load-ит каждую не-hero картинку для сокращения бандла — LCP остаётся нормальным, но hero случайно попал и LCP регрессирует на 800 мс. Ещё одна команда шипает полный SPA и получает зелёные vitals при первой загрузке — но каждый клиентский переход маршрута невидим для метрик. Один бюджет, три метрики, четыре команды и ни одной ручки.

Метрики — не независимые ручки.

Фикс одной Core Web Vital может навредить другой, и senior-ход — видеть систему, а не отдельные метрики. Частые трейдоффы:

  • Инлайнинг большого блока критического CSS помогает render delay LCP (нет render-blocking запроса), но раздувает HTML, вредя TTFB. Итоговый эффект зависит от скорости соединения и размера HTML.
  • Lazy-load всего для сокращения бандла помогает INP (меньше JS парсить и выполнять), но, применённый случайно к hero-картинке, добавляет большой load delay и рушит LCP.
  • Резервирование щедрого места для CLS — большие min-height контейнеры для рекламы — может опустить LCP-элемент ниже сгиба, и он перестаёт быть LCP-кандидатом. Иногда это нормально; иногда нет.
  • Отгрузка большого интерактивного фреймворка ради отзывчивого ощущения добавляет гидратацию — одну большую длинную задачу, взрывающую INP для ранних взаимодействий.

Единой ручки нет. Дисциплина: измерять все три до и после любого изменения, в поле где возможно, в throttled-лабе как минимум. Изменение, улучшающее одну метрику за счёт другой, может быть оправдано или нет — но знать это нельзя без взгляда на все три.

Атрибуция INP в поле: от метрики к строке кода.

Полевое число INP само по себе не actionable — это задержка без прикреплённой причины. Цепочка атрибуции:

  1. PerformanceObserver, подписанный на event записи, даёт вам для каждого взаимодействия разбивку input delay / processing time / presentation delay.
  2. Long Animation Frames (long-animation-frame записи через PerformanceObserver) дают для кадра, в который попало медленное взаимодействие, массив выполнившихся скриптов — с URL исходников, именами функций и длительностями.
  3. Совмещайте: INP с высоким input-delay → найдите длинную задачу в записи LoAF; с высоким processing → обработчик, атрибутированный LoAF; с высоким presentation delay → тяжёлый рендер или принудительная синхронная компоновка в обработчике.

Весь смысл встройки LoAF в RUM — превратить «p75 INP 340 мс» в «функция фильтра поиска на search.js:88 — доминирующий скрипт в медленных кадрах». Это actionable.

Атрибуция LCP в поле.

Колбэк onLCP библиотеки web-vitals доставляет не только время LCP, но и элемент (чтобы знать, что браузер выбрал как LCP), URL ресурса (если картинка) и четырёхчастную разбивку: TTFB, load delay, load time, render delay. Логирование этого в телеметрию означает, что регрессия «LCP вырос с 1.9 с до 3.8 с» немедленно показывает, какая фаза выросла — был ли это TTFB (медленный деплой), load delay (случайный loading="lazy"), load time (неоптимизированная картинка) или render delay (новый render-blocking скрипт)?

Атрибутированный field-пример из web-vitals RUM
LCP-элемент
img.hero
LCP фаза: load time
3510 мс (доминирует — JPEG 4 МБ)
LCP фазы: TTFB + delay + render
610 мс в сумме
INP
38 мс — хорошо
CLS
0.02 — хорошо

Цикл RUM и CI — оба нужны.

Полный production-observability-стек состоит из двух половин.

RUM: отгрузить библиотеку web-vitals (или аналог), использующую PerformanceObserver для захвата LCP, INP и CLS ровно так, как Chrome их оценивает, плюс атрибуцию (элемент, разбивка фаз, цель взаимодействия, сдвигающиеся узлы). Отправлять в телеметрию с тегами маршрут, класс устройства, страна, релиз. Это реальный вердикт — ловит регрессии, которые лаб никогда не раскроет, особенно регрессии класса устройств и взаимодействия, которые запускают только реальные пользователи.

CI: синтетический гейт — Lighthouse CI или Playwright-трасса — на каждом PR, throttled под реалистичное mid-tier устройство, с бюджетами по LCP, total blocking time (lab-прокси для INP) и CLS, падающий на регрессии.

Ни одного в отдельности недостаточно. Лаб без RUM шипает регрессии, которые раскрывают только реальные устройства. RUM без CI-гейта означает, что каждая регрессия достигает production до того, как кто-то её замечает.

Разрыв soft-navigation — почему SPA-vitals требуют явной инструментации.

Core Web Vitals разрабатывались для полных загрузок страниц. В SPA первая загрузка имеет реальный LCP — но последующие клиентские переходы маршрутов («мягкие навигации») исторически не имели измерения LCP вообще, потому что не срабатывает событие document load. Для пользователя мягкая навигация ощущается ровно как загрузка страницы — он кликнул ссылку и ожидает быстрого нового контента — но метрика этого не видела.

Chrome шипает поддержку soft-navigation для атрибуции LCP и других vitals клиентским переходам маршрутов, но покрытие ещё созревает. Следствие: для SPA не предполагайте, что история vitals завершена только потому что числа первой загрузки зелёные. Быстрая первая загрузка + вялые переходы маршрутов — реальный, частый и исторически недоизмеренный провал. Инструментируйте мягкие навигации явно с PerformanceObserver soft-navigation записями или собственными RUM-марками на router.beforeEach / событиях смены маршрута.

Почему это работает

CLS session window (худший 5-секундный кластер вместо пожизненной суммы) — конкретный пример того, как спецификация эволюционирует к репрезентации реального пользовательского опыта, а не просто чистого инженерного измерения. Оригинальный lifetime-sum CLS несправедливо наказывал долго живущие страницы и infinite scroll — сдвиг, произошедший через четыре минуты после загрузки, считался так же как на второй секунде. Session window фокусирует метрику на концентрированном плохом поведении: всплеск сдвигов при перезагрузке рекламы или пакет картинок без размеров грузится. Session window более репрезентативен для того, что пользователь реально замечает в контексте.

Найди ошибку
log
[LCP] value: 4120 ms  rating: poor
element: img.hero
url: /assets/hero-original.jpg  (3.8 MB, JPEG, 4000x3000)
phase split: ttfb 280ms | loadDelay 90ms | loadTime 3510ms | renderDelay 240ms

[INP] value: 38 ms  rating: good
[CLS] value: 0.02  rating: good

Прочитайте разбивку по фазам и детали элемента — какая фаза доминирует, какова первопричина, и каков фикс? Что команде НЕ делать, и почему?

Какой RFC?

Какой браузерный API является основой для измерения LCP, INP, layout shifts и Long Animation Frames в real-user monitoring?

Спроектируй

Спроектируйте стратегию Core Web Vitals для медиасайта: страницы статей с hero-картинкой, рекламой, эмбедами и секцией комментариев. Добейтесь хорошего LCP, INP и CLS на p75 в поле, и держите их зелёными со временем.

  • Hero-картинка — LCP-элемент на каждой статье.
  • Реклама и социальные эмбеды загружаются поздно и в тело статьи.
  • Секция комментариев интерактивна и ниже сгиба.
  • Сайт server-rendered и гидрируется.
  • Цели: LCP ≤2.5 с, INP ≤200 мс, CLS ≤0.1 — все на field p75.
  • Регрессии должны быть пойманы до production.
Викторина

Почему страница может показывать отличный Lighthouse-score, но всё равно быть флагнутой за плохие Core Web Vitals в Search Console?

Вспомните перед уходом
  1. 01
    LCP страницы плох. Опишите, как диагностировать через разбивку фаз, и почему чтение разбивки — ключевой шаг.
  2. 02
    Опишите полный production observability-стек для Core Web Vitals — что даёт RUM, что CI, и почему ни одного в отдельности недостаточно.
  3. 03
    Почему INP и материал по SSR/гидратации неразделимы, и что говорит паттерн «только в первые секунды»?
Итог

Три Core Web Vitals разделяют бюджет производительности: инлайн CSS помогает render delay LCP, но вредит TTFB; lazy-load уменьшает бандл, но может добавить LCP load delay; щедрое место для CLS может изменить LCP-кандидат. Каждое изменение нужно измерять по всем трём метрикам, а не одной в изоляции. Полная production-observability требует как RUM (библиотека web-vitals + PerformanceObserver, отправляющая атрибутированные LCP/INP/CLS в телеметрию с тегами по маршруту и классу устройства), так и throttled CI-гейта (Lighthouse CI или Playwright, бюджеты LCP/TBT/CLS на каждый PR). RUM — реальный вердикт; CI ловит регрессии до production. INP в SPA дополнительно усложняется мягкими навигациями, исторически не имевшими атрибуции LCP — инструментируйте смены маршрутов явно. Именно CrUX field p75 определяет ранжирование; lab-фикс, не сдвинувший его, не помог реальным пользователям.

Связанные уроки
встречается в267
Продолжить восхождение ↑Core Web Vitals: тест с выбором ответа
хоткеи развернуть
поиск
K
пред. пьеса
k
след. пьеса
j
тиры
t
это меню
?
sources4
expand
  1. 01
  2. 02
  3. 03
  4. 04

Trademarks belong to their respective owners. Editorial reference only.