План сбора полевых метрик
В первых двенадцати уроках мы исследовали отдельную загрузку и взаимодействие. Теперь возникает другая задача: понять, какие условия встречаются у аудитории и насколько часто заметная задержка повторяется. Для этого используют RUM, или наблюдение производительности при настоящих посещениях. Но прежде чем собирать значения, нужно определить, какую ситуацию описывает каждая запись.
Результат этого урока — договор данных и небольшой локальный диагностический наблюдатель. Он ничего не отправляет, не подключает аналитический сервис и не является внедрённым мониторингом ProfessorWeb. Исходники дополнения доступны в архиве advanced-lab. Времена собственного устройства читатель получает отдельно; учебный CSV не подставляется вместо них.
Запись должна отвечать на вопрос
Представим библиотеку с обычными статьями и каталогом. У статьи важен показ текста, у каталога — реакция на фильтр. Если сохранить только число задержки без типа страницы, трудно понять, куда направлять исследование. Если сохранить весь адрес с параметрами, отчёт получит сведения, которые не нужны для этого решения. Начнём с небольшого известного набора категорий.
Условная запись содержит версию оформления, шаблон, тип навигации, начальную видимость документа и сведения о поддержке наблюдаемого API. Версия позволяет разделить исходники до и после изменения. Шаблон объединяет страницы с похожей технической структурой. Тип навигации отделяет первоначальное получение документа от других действий. Начальная видимость помогает заметить, что вкладка открывалась в фоне.
В advanced/diagnostics/collector.js используются постоянные учебные метки lab-a и article. Они не считывают поисковый запрос, название открытого материала или идентификатор пользователя. На настоящем сайте категории должны поступать из контролируемой конфигурации шаблона. Не создавайте их автоматически из любой строки URL: иначе параметры и нестабильные адреса станут отдельными группами.
Для обсуждения структуры достаточно следующего фрагмента записи. Это схема, а не полученный отчёт:
{
"schema": "diagnostic-v1",
"release": "lab-a",
"template": "article",
"initialVisibility": "visible",
"navigationType": "navigate",
"lcpCandidateMs": null,
"shifts": [],
"longTasks": [],
"stopReason": null
}
Поле schema обозначает договор интерпретации. Если позже изменится алгоритм расчёта, нельзя без пояснения объединить старые и новые значения. Иначе изменение наблюдателя будет выглядеть как изменение сайта. Рядом с версией продукта сохраняйте версию измерительного метода и дату его применения.
Возможности браузера и отсутствие значения
PerformanceObserver позволяет получать определённые типы записей, но набор поддерживаемых типов зависит от браузера. Поэтому код проверяет supportedEntryTypes отдельно для LCP-кандидатов, сдвигов и длинных задач. Проверка наличия самого конструктора недостаточна. Назначение этого списка описано в MDN: supportedEntryTypes.
У неподдерживаемого типа запись не появляется. Это не нулевая задержка и не отсутствие проблемы. Такой случай должен остаться отдельным состоянием. В нашем объекте есть карта support, а первоначальное значение кандидата равно null. Если API доступен, но подходящее событие ещё не пришло, результат также нельзя превращать в ноль.
Локальный наблюдатель использует buffered: true, чтобы получить доступные ранее созданные записи соответствующего типа. Это помогает при подключении после начала документа, но не восстанавливает сведения, которых браузер не предоставлял. Параметр не даёт универсальной полноты истории. Способ подключения описан в MDN: PerformanceObserver.
Для сдвига сохраняются значение события и признак недавнего ввода. Для длинной задачи — длительность. Наблюдатель ограничивает каждый массив сорока записями и отмечает обрезание через clipped. Это учебный предел памяти, а не статистический стандарт. Обрезанный набор нельзя считать полным посещением: самые поздние события уже могли не попасть в него.
Граница жизни документа
Посещение не всегда заканчивается закрытием вкладки. Человек может перейти к другому материалу, скрыть браузер или позже вернуться по истории. Если продолжать складывать значения без границ, запись объединит разные состояния. Для полноценного мониторинга потребуется заранее заданный договор таких переходов, а не только обработчик последнего события.
В учебном наблюдателе выбрана простая граница: первый видимый сегмент документа заканчивается при первом скрытии или pagehide. Перед отключением код забирает ожидающие записи через takeRecords(). Если документ изначально скрыт, сбор этого сегмента не начинается. Так исходники показывают явное ограничение, которое можно объяснить, вместо молчаливой попытки охватить любое посещение.
После возвращения из bfcache наблюдатель не начинает новый сегмент. Это намеренное ограничение примера, которое нельзя переносить в полноценный сборщик без изменения. Восстановленное содержимое и жизненный цикл метрик разберём в следующих уроках. Сейчас результат должен читаться буквально: локальный снимок первого разрешённого сегмента, а не окончательная история пользователя.
Кнопка «Показать локальную запись» выводит JSON в поле страницы. Повторное нажатие показывает текущий объект, а не создаёт второе посещение. Обработчик не использует fetch, sendBeacon, cookies или хранилище. Поэтому пример позволяет изучить состав данных до любого решения о доставке и хранении аналитики.
Почему это ещё не Core Web Vitals
Последний доступный LCP-кандидат не равен универсально корректной итоговой метрике для всех способов открытия страницы. Сумма отдельных сдвигов также не является полным CLS. Наш наблюдатель вообще не рассчитывает INP. У этих метрик есть правила завершения, жизненного цикла, взаимодействий и восстановления документа. Их определения уже рассмотрены в уроке о LCP, сдвигах и INP.
Для реального проекта полезно изучить специализированную библиотеку web-vitals, которая учитывает такие особенности. Выбор библиотеки не отменяет договор данных: всё ещё нужны область наблюдения, версия, обработка пропусков и правила передачи. В этом уроке библиотека не подключена и аналитический endpoint не создан.
Отдельная длинная задача — диагностический сигнал работы основного потока, а не готовый INP. Она может выполняться, когда человек ничего не нажимает. Для объяснения плохого отклика нужно связать её с конкретным взаимодействием и следующим кадром. Семантика таких записей описана в MDN: Long Tasks API.
От локального объекта к будущему плану
Продумайте единицу наблюдения до расчёта распределения. Одна запись на документ, несколько снимков одной загрузки и одна запись на взаимодействие имеют разные знаменатели. Если самый активный читатель создаёт много строк, обычное среднее строк будет сильнее отражать его действия. Это может быть полезной задачей, но её нельзя назвать распределением посещений без уточнения.
Если будущий мониторинг использует выборочный сбор, зафиксируйте правило отбора до просмотра значений. Отбор только медленных событий удобен для поиска причин, однако не описывает частоту медленных посещений во всей аудитории. Равномерный случайный отбор документов отвечает на другой вопрос. Нельзя вычислять общий процент хороших посещений из диагностической коллекции, куда быстрые случаи намеренно не попадали.
Для решения о хранении полезно заранее назвать срок, размер записи и ответственного за изменение схемы. Подробное устройство аналитики относится к курсу веб-аналитики. Здесь важно техническое следствие: лишние поля увеличивают разнообразие групп и усложняют сравнение, а изменение структуры требует явного перехода между версиями.
Завершите урок собственным договором одного наблюдения: что измеряется, когда сегмент заканчивается, какие значения отсутствуют и какой вывод запись позволяет сделать. Локальный JSON должен соответствовать этому договору. В следующем уроке возьмём ограниченный синтетический набор и разберём, как процентиль и состав группы меняют вывод даже при совершенно правильной арифметике.