Лабораторные и полевые данные
Почему «у меня 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-му перцентилю: среднее и медиана прячут именно тот хвост, ради которого вы и оптимизируете.