open atlas
↑ К треку
React с нуля до senior RCT · 14 · 02

RUM и полевое профилирование: лаборатория врёт, поле решает

Подключите onLCP, onINP, onCLS к маяку с роутом, релизом и классом устройства — без этих измерений полевые данные ни на что не отвечают. Атрибуция INP делит задержку входа, обработку и отрисовку. Семплируйте React Profiler на 1 проценте, алерты по p75 на кольцо релиза, без PII.

RCT Senior ◷ 18 min
Уровень
ОсновыJuniorMiddleSenior

Вопрос был простой: «Какой релиз замедлил чекаут?» Ответ занял три недели. У команды были маяки vitals — значение, имя метрики, таймстамп — и четырнадцать деплоев в подозрительном окне. Ни тега релиза на маяке, ни роута, ни класса устройства. Они коррелировали таймстампы деплоев с часовыми агрегатами INP, получили трёх правдоподобных подозреваемых, переразвернули каждого по отдельности на staging-кольцо без живых пользователей, не воспроизвели ничего — и наконец нашли виновника бисект-редеплоями продакшена за четыре ночи: бамп зависимости дейтпикера, удвоивший время обработки ввода, но только на среднебюджетных Android — поэтому staging на ноутбуках M-серии оставался зелёным, а Lighthouse печатал 96. После инцидента в маяк добавили три поля: роут, релиз, класс устройства. Следующий вопрос «какой релиз регрессировал» стал одним запросом и занял тридцать секунд. Лаборатория говорит, что может быть медленным; только поле говорит, что медленно на самом деле, у кого и с какого момента.

Лаборатория врёт, поле решает

Прогон Lighthouse на вашей машине — одна выборка из распределения железа, в котором ваши пользователи не живут. Когда вы спорите с полевыми данными, прикрываясь Lighthouse-оценкой, — это именно та путаница, которую разбирает этот урок. CPU медианного пользовательского устройства в разы медленнее ноутбука разработчика; поле добавляет задушенные сети, телефоны с 4 ГБ RAM, режим энергосбережения, двадцать открытых вкладок, блокировщики рекламы и сессии по восемь часов вместо лабораторных девяноста секунд. Лабораторные инструменты (Lighthouse, WebPageTest, локальный Profiler) остаются необходимыми — они контролируемы, а значит, ловят регрессии до релиза на фиксированном устройстве и воспроизводят их детерминированно. Но они отвечают на вопрос «может ли это быть медленным?», и никогда — «медленно ли это у настоящих пользователей?». Полевые данные — Real User Monitoring (RUM, мониторинг реальных пользователей) — это та истина, на которой построена программа Core Web Vitals: Chrome агрегирует LCP, INP и CLS реальных сессий по origin, и проходной балл — p75 реальных просмотров страниц. Senior-команда использует оба мира и не путает их роли: лаборатория — пререлизный регрессионный гейт, поле — вердикт.

Подключение web-vitals: три измерения или шум

Библиотека web-vitals — стандартный инструмент: колбэки onLCP, onINP, onCLS срабатывают, когда метрика финализируется, — а для INP и CLS это поздняя стадия жизненного цикла страницы, поэтому отправка обязана сбрасываться на visibilitychange/pagehide через sendBeacon, а не на load. Сама проводка — двадцать строк; данные делают пригодными к действию измерения, которые вы прикрепляете:

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

function send(metric) {
  navigator.sendBeacon('/vitals', JSON.stringify({
    name: metric.name,            // 'INP' | 'LCP' | 'CLS'
    value: metric.value,
    rating: metric.rating,        // 'good' | 'needs-improvement' | 'poor'
    route: routePattern(),        // измерение 1: '/orders/:id', НЕ сырой URL
    release: __RELEASE__,         // измерение 2: id сборки, внедрённый при деплое
    device: deviceClass(),        // измерение 3: грубая корзина: память + ядра
    attr: summarize(metric.attribution),
  }));
}
onINP(send); onLCP(send); onCLS(send);

Роут говорит, какая поверхность регрессировала, — глобальное число усредняет маркетинговую страницу с дата-гридом до бессмыслицы. Релиз превращает «когда-то стало медленно» в «кольцо N на 40% хуже кольца N−1» — вопрос о регрессировавшем релизе становится одним group-by. Класс устройства отделяет «мы выкатили регрессию» от «ретейл-кампания привела трафик с дешёвых Android» — сдвиг структуры трафика двигает глобальные перцентили без единого изменения кода. Отсутствие любого из трёх схлопывает разные популяции в среднее, неспособное отвечать на вопросы; это и есть трёхнедельная охота из вступления.

Викторина

Глобальный p75 INP на дашборде — 180 мс, зелёный. Поддержка продолжает эскалировать лаги страницы поиска у пользователей Android. Что не так с измерением?

Атрибуция INP: куда ушли миллисекунды

INP — это примерно худшее взаимодействие просмотра страницы (для насыщенных страниц — высокий перцентиль всех взаимодействий, так что один плохой клик среди сотен всё равно считается). Attribution-сборка делит каждое взаимодействие-кандидат на три фазы, и это деление решает, где работать: задержка входа (input delay) — событие лежало в очереди, потому что главный поток был занят чем-то другим (гидрация, обновление чарта, сторонний скрипт); длительность обработки (processing) — выполнялись ваши обработчики, включая синхронный рендер и коммит React, который они запускают; задержка отрисовки (presentation) — кадр после обработчиков шёл долго (layout, paint, слишком много DOM). Атрибуция также называет целевой элемент и тип события. React-специфичное чтение: толстый слой processing на нажатиях клавиш — это обычно большой синхронный каскад рендера, и лечится он мемоизацией, виртуализацией списков или переносом обновления в transition. Толстая задержка входа на кликах сразу после навигации — это обычно гидрация или парсинг данных, оккупировавшие поток, и оптимизация обработчика там не даёт ничего. Обсерверы Long Tasks и более новый Long Animation Frames (PerformanceObserver с типом long-animation-frame) атрибутируют занятый поток конкретным скриптам, замыкая вопрос «занят чем?».

React Profiler в продакшене, по сэмплу

Колбэк onRender компонента Profiler отдаёт тайминги по коммитам: id, phase (mount/update), actualDuration (время рендера поддерева в этом коммите), baseDuration (оценка полного рендера без мемоизации). Продакшен-сборки по умолчанию вырезают профилирование; включение означает поставку profiling-сборки (react-dom/profiling) с накладными расходами в несколько процентов. Дисциплина, делающая это жизнеспособным: семплируйте сессии, а не коммиты — включайте профилирование примерно для 1% сессий, выбранных на старте сессии, отправляйте агрегированный actualDuration по id профайлера и тегируйте данные каждой сессии теми же тремя измерениями. Полная трассировка каждой сессии слишком тяжела дважды: рантайм-надбавка ложится ровно на те медленные устройства, которые вам важны, а объём маяков стоит дороже, чем даваемое им знание. Один процент от миллионов сессий даёт плотные перцентильные оценки по кольцам релиза — достаточно, чтобы увидеть, что коммиты OrdersTable стали в 3 раза медленнее в релизе 142, не профилируя ни одного лишнего пользователя.

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

Почему p75, а не p50 или p99? p50 объявляет победу, пока четверть пользователей страдает, — «половине трафика нормально» это низкая планка. В p99 доминирует то, что вы не почините: умирающие батареи, фоновые вкладки, антивирусные сканы, роуминг на 2G — погоня за ним сжигает кварталы на шум. p75 — осознанный компромисс, стандартизованный программой Core Web Vitals: улучшив его, вы доказуемо улучшили опыт большинства пользователей, а цель остаётся достижимой и атрибутируемой вашему коду. Команды с насыщенными интерактивными приложениями часто ведут p95 внутренне как серию раннего предупреждения — но алерты и цели ставят на p75.

Перцентили по кольцам, алерты по ущербу и граница приватности

Запрос, отвечающий «какой релиз регрессировал»: p75 INP по роуту, сгруппированный по кольцу релиза, кольцо к кольцу — канарейка на 1% даёт ответ до полного раската, если трафик пробивает минимальный размер выборки. Дизайн алертов следует одному принципу: будить из-за ущерба пользователям, а не из-за метрик. Алерт, достойный пейджера, — конъюнкция: p75 роута выше порога И держится N минут И трафик выше пола — потому что каждое условие поодиночке производит шум: перцентили на тонком трафике дико болтаются, мгновенные всплески самоизлечиваются, а один медленный синтетический проб — не инцидент. Мягкий дрейф (p75 ползёт на 5% в неделю) уходит в канал еженедельного ревью, а не в пейджер. И граница приватности не обсуждается: перф-маяки не несут никакого PIIпаттерны роутов вместо сырых URL (квери-строки текут токенами и почтами), грубые корзины устройств вместо отпечатков, никаких id пользователей. Перф-маяк, способный идентифицировать пользователя, — это инцидент по защите данных, ждущий своего аудитора; проектируйте схему так, чтобы вопрос не возникал вовсе.

Викторина

Атрибуция INP для кликов по строке заказов: задержка входа 420 мс, обработка 35 мс, отрисовка 20 мс. Команда планирует спринт мемоизации пути обработчика клика. Что на самом деле говорит атрибуция?

Вспомните перед уходом
  1. 01
    Назовите три измерения, нужные каждому vitals-маяку, и какой вопрос открывает каждое из них.
  2. 02
    Расшифруйте измерение INP: дайте три фазы деления атрибуции и отдельное лечение, на которое указывает каждая.
Итог

Лабораторные и полевые инструменты отвечают на разные вопросы, и senior-команда никогда не меняет их роли местами: Lighthouse и локальное профилирование — контролируемые детекторы регрессий, говорящие, что может быть медленным; Real User Monitoring — вердикт о том, что медленно, у кого и с какого момента, поэтому Core Web Vitals определены на полевых данных как p75 реальных просмотров. Инструмент — библиотека web-vitals: onLCP, onINP, onCLS со сбросом через sendBeacon на pagehide, потому что INP и CLS финализируются поздно, — а ценность данных решают три измерения маяка: паттерн роута (какая поверхность), релиз (какой деплой — p75 кольцо к кольцу делает вопрос о регрессировавшем релизе одним запросом) и класс устройства (регрессия против сдвига структуры трафика). INP читается через деление атрибуции: задержка входа — борьба за главный поток с чужой работой, и указывает на атрибуцию Long Task/LoAF; обработка — ваши обработчики плюс запущенный ими синхронный рендер, и указывает на мемоизацию или transitions; отрисовка — стоимость пейнта, и указывает на размер DOM. React Profiler работает в продакшене через profiling-сборку с маяками onRender, но семплированно — около 1% сессий: полная трассировка облагает налогом ровно те медленные устройства, что вам важны, и топит вас в маяках, а семплированные перцентили по кольцам всё равно точно показывают трёхкратную регрессию коммитов. Алерты будят по ущербу пользователям и никогда по одиночным метрикам: порог И стойкая длительность И пол трафика, а медленный дрейф уходит в ревью, не в пейджер. Граница приватности структурна: паттерны роутов вместо URL, грубые корзины устройств, никаких id — перф-маяк, способный идентифицировать пользователя, это инцидент, который вы запланировали на потом. Теперь, когда встретишь жалобу на лаг, которую Lighthouse не подтверждает, — тяни первым делом трёхмерный запрос: роут, релиз, класс устройства; ответ почти всегда в сегменте, который глобальное среднее скрывало.

Практика

Начни сверху. Задачи идут от простого к сложному: вспомнить факт, применить к случаю, затем senior-уровень. Открой, попробуй, потом открой ответ.

вспомнитьприменитьуглубить0 из 6 завершено

Что-то непонятно?

Задай вопрос по этому уроку. Вопросы анонимны и попадают напрямую автору — урок станет лучше.

Примени это

Примени этот урок в реальном проекте.

хоткеи развернуть
поиск
K
пред. пьеса
k
след. пьеса
j
тиры
t
это меню
?
sources4
expand
  1. 01
  2. 02
  3. 03
  4. 04

Trademarks belong to their respective owners. Editorial reference only.