Лабораторные и полевые данные

Почему «у меня 100 баллов в Lighthouse» и «у пользователей красный LCP» — это два совместимых факта, а не противоречие.

Лабораторные данные (lab) — измерение в контролируемой среде на одном устройстве. Полевые данные (field, они же RUM) — то, что реально произошло у настоящих людей на их телефонах и сетях. Первое отвечает «почему медленно», второе — «медленно ли вообще».

Зачем различать

Классическая сцена. Разработчик прогоняет Lighthouse, получает 98 баллов, вешает скриншот в чат. Через неделю приходит Search Console: «Core Web Vitals: LCP требует улучшения на 8 400 URL». Кто-то врёт? Нет. Просто это два разных измерения, отвечающих на разные вопросы, и путать их — самая дорогая ошибка в производительности.

Лаборатория воспроизводима: один и тот же прогон, одна и та же сеть, чистый профиль, пустой кеш. Именно поэтому она годится для отладки и для CI: изменение числа между двумя прогонами что-то значит. Но она в принципе не может сказать, быстро ли у людей — потому что людей в ней нет.

Поле — наоборот: правда о пользователях, но правда запаздывающая (данные копятся неделями) и неудобная для отладки. Оно скажет, что LCP на карточке товара 4,3 секунды, но не покажет вам водопад загрузки.

Правильный цикл: поле находит проблему → лаборатория объясняет причину → поле подтверждает, что починили. Начинать с лаборатории — значит оптимизировать то, что вы выбрали интуицией. Снова гадание.

Почему 100 баллов ничего не гарантируют

Первое: балл Lighthouse — не метрика, а взвешенная композиция лабораторных метрик. В актуальных версиях веса такие:

МетрикаВес в баллахКомментарий
Total Blocking Time (TBT)30%лабораторный заменитель INP
Largest Contentful Paint (LCP)25%совпадает с полевой по смыслу
Cumulative Layout Shift (CLS)25%только сдвиги во время автопрогона
First Contentful Paint (FCP)10%вспомогательная
Speed Index (SI)10%вспомогательная

Обратите внимание: INP в этом списке нет вообще. Lighthouse в стандартном режиме просто загружает страницу и ничего в ней не нажимает — измерять отзывчивость ему не на чем. Вместо неё берётся TBT: сумма «лишних» миллисекунд у длинных задач при загрузке. Корреляция с INP есть, но это оценка потенциала, а не факт. Страница с идеальным TBT может отвечать на клик по фильтру полторы секунды — Lighthouse этого не заметит.

Второе: CLS в лаборатории считается за несколько секунд автоматического прогона. Баннер, который приезжает после скролла, и сдвиг от лениво загруженного блока в лабораторию не попадут.

Третье, самое приземлённое: прогон в DevTools идёт на вашем процессоре. Даже с включённым троттлингом (искусственным замедлением) мощная машина «переваривает» JS иначе, чем реальный телефон.

И четвёртое: одиночный прогон шумит. Разброс LCP между запусками на одной и той же странице легко достигает 15–20%. Один зелёный прогон — это не результат, это одна точка.

Откуда берутся полевые данные

Есть два источника, и нужны оба.

CrUX — Chrome User Experience Report

Публичный набор данных Google: анонимная телеметрия от пользователей Chrome, согласившихся на отправку статистики. Именно CrUX — источник истины для отчёта Core Web Vitals в Search Console и для «Discover what your real users are experiencing» в PageSpeed Insights. Ключевые особенности:

  • Скользящее окно 28 дней. Значит, эффект вашего релиза проявится не завтра, а через 3–4 недели.
  • Данные есть только для URL с достаточным трафиком. Если конкретной страницы нет — показывается агрегат по всему домену (origin).
  • Только Chrome и только реальные пользователи. Ни Safari, ни iOS-браузеров там нет.

Дёрнуть CrUX можно и напрямую, API открыт:

curl -s "https://chromeuxreport.googleapis.com/v1/records:queryRecord?key=$CRUX_KEY" \
  -H "Content-Type: application/json" \
  -d '{
        "url": "https://example.com/product/42",
        "formFactor": "PHONE",
        "metrics": ["largest_contentful_paint", "interaction_to_next_paint", "cumulative_layout_shift"]
      }'

В ответе — гистограмма по трём корзинам (хорошо / надо улучшать / плохо) и то самое значение 75-го перцентиля:

Результат:

largest_contentful_paint
  хорошо        (0–2500 мс)     58,1%
  надо улучшать (2500–4000 мс)  27,4%
  плохо         (4000+ мс)      14,5%
  p75 = 3312 мс   ← оценка страницы: «надо улучшать»

interaction_to_next_paint
  p75 = 168 мс    ← хорошо

cumulative_layout_shift
  p75 = 0,04      ← хорошо

Читается так: больше половины визитов быстрые, но каждый седьмой пользователь ждал больше четырёх секунд. Оценку странице ставит не большинство, а p75.

Собственный RUM

CrUX — это средняя температура по Chrome с задержкой в месяц. Свой RUM (Real User Monitoring) даёт данные сегодня и с той разбивкой, которая нужна вам: по шаблону страницы, по стране, по типу устройства, по авторизованным и гостям, по A/B-варианту. Собирается той же библиотекой web-vitals из прошлого урока — только берите сборку с атрибуцией, она сразу присылает причину:

import { onLCP } from 'web-vitals/attribution';

onLCP((metric) => {
  const a = metric.attribution;

  navigator.sendBeacon('/rum', JSON.stringify({
    name: metric.name,
    value: metric.value,          // сам LCP
    rating: metric.rating,
    template: window.__PAGE_TEMPLATE__, // 'product' | 'catalog' | 'home'

    // из чего он сложился — чинить можно без гадания:
    target: a.target,             // CSS-селектор LCP-элемента
    url: a.url,                   // адрес картинки
    ttfb: a.timeToFirstByte,
    loadDelay: a.resourceLoadDelay,
    loadDuration: a.resourceLoadDuration,
    renderDelay: a.elementRenderDelay,
  }));
});

Именно эти четыре числа превращают «LCP плохой» в конкретную задачу. Большой ttfb — идти к бэкенду и кешу. Большой loadDelay — картинка обнаружена поздно, нужен preload или fetchpriority="high". Большой loadDuration — картинка тяжёлая. Большой renderDelay — что-то блокирует отрисовку.

Как это работает: перцентиль 75

Почему не среднее? Потому что среднее прячет именно тех, ради кого всё затевалось. Возьмём десять визитов на одну страницу (LCP в секундах):

1,1  1,2  1,3  1,4  1,5  1,6  1,8  4,2  5,0  6,5

среднее   = 2,56 с   → «почти зелено, всё нормально»
медиана   = 1,55 с   → «да у нас отлично!»
p75       = 4,2  с   → КРАСНАЯ зона

Семь человек из десяти открыли страницу за полторы секунды. И трое — те, кто был на слабом телефоне в плохой сети, — ждали от четырёх до шести с половиной секунд. Среднее и медиана про них молчат. 75-й перцентиль — специально выбранный компромисс: он не гонится за самым несчастным пользователем (это был бы p99 и вечно красный дашборд), но и не даёт спрятать проблемный хвост за счастливым большинством.

Правило страницы: она считается прошедшей Core Web Vitals, если по всем трём метрикам p75 попал в зелёную зону. Не «в среднем», не «по большинству» — по всем трём.

ПризнакЛаборатория (Lighthouse, DevTools, WebPageTest)Поле (CrUX, свой RUM)
Отвечает на вопроспочему медленномедленно ли вообще и у кого
Воспроизводимостьвысокаяникакой — это живые люди
Обратная связьсекундыот суток (RUM) до 28 дней (CrUX)
INPне измеряется (только TBT-заменитель)измеряется честно
Годится для CIданет
Влияет на ранжированиенетда

Частые ошибки

  • Отчитываться баллом Lighthouse. Балл — композитный индикатор для отладки. Google ранжирует по полевым Core Web Vitals, а не по нему.
  • Мерить на своём MacBook и делать выводы. Без троттлинга CPU и сети вы измеряете свой ноутбук, а не сайт.
  • Верить одному прогону. Гоняйте 3–5 раз и берите медиану, иначе поймаете шум и будете чинить его.
  • Смотреть только origin-агрегат в CrUX. Домен может быть зелёным за счёт лёгкой главной, пока карточка товара — а это половина трафика — красная.
  • Ждать эффекта релиза на следующий день. В CrUX окно 28 дней: сразу после выката вы увидите смесь старого и нового. Свой RUM покажет правду за сутки — ещё одна причина его завести.
  • Считать, что раз в лаборатории INP нет, то и проблемы нет. Отзывчивость проверяется руками: кликните по самому тяжёлому элементу с включённым троттлингом CPU.

Итоги

  • Лаборатория воспроизводима и объясняет причины; поле показывает правду о пользователях. Нужны оба, в таком порядке: поле → лаборатория → поле.
  • Балл Lighthouse — взвешенная сумма лабораторных метрик, где 30% занимает TBT, а INP отсутствует. 100 баллов не значат, что людям быстро.
  • CrUX — полевые данные Chrome за скользящие 28 дней, источник для Search Console и PageSpeed Insights. При нехватке трафика показывает агрегат по домену.
  • Свой RUM на web-vitals/attribution даёт данные завтра и с разбивкой по шаблонам, плюс сразу присылает причину плохого LCP.
  • Оценка — по 75-му перцентилю: среднее и медиана прячут именно тот хвост, ради которого вы и оптимизируете.
Проверьте себя
1. Lighthouse показывает 100 баллов, а Search Console пишет, что INP на этих же страницах «плохо». Наиболее вероятное объяснение?
ASearch Console ошибается, надо подождать пересчёта
BLighthouse в стандартном режиме не измеряет INP: он только загружает страницу и не нажимает в ней ничего, поэтому тормозящие обработчики кликов в балл не попали
CБалл 100 означает зелёные Core Web Vitals по определению, значит, сайт настроен неверно
DОдна из метрик просто не успела собраться в лаборатории
2. У страницы среднее LCP 2,4 с, медиана 1,6 с, а 75-й перцентиль 4,3 с. Как Google оценит страницу по LCP?
AХорошо — среднее и медиана в зелёной зоне
BНадо улучшать — берётся среднее между медианой и p75
CПлохо — оценка ставится по 75-му перцентилю, а 4,3 с выше порога 4,0 с
DОценка не ставится, пока метрики противоречат друг другу