Слово «медленно» нужно превратить в наблюдение

Жалоба «страница долго открывается» может означать белый экран до ответа, позднее появление главного блока, зависание после нажатия, скачущий layout или медленное завершение формы. Эти проблемы имеют разные измерения и разные исправления. Запишите путь целиком: URL, шаг до него, авторизован ли пользователь, первый или повторный визит, тип устройства, примерную сеть и время появления симптома. Если проблема возникает после входа, публичный Lighthouse главной страницы её не воспроизведёт. Если она характерна для первого визита, тёплый локальный кэш разработчика исказит картину.

Сделайте минимум три одинаковых прогона и сохраните не средний score, а артефакты: waterfall, performance trace, серверный request ID и временные метки. Разброс тоже является данными. Один редкий медленный ответ может указывать на холодный cache, конкретный backend path или третью сторону. Постоянно длинная main-thread задача указывает в другую ветку. Не очищайте кэш и не включайте throttling автоматически: сначала определите, какое состояние вы проверяете.

Дерево диагностики и бюджет производительности

Проходите дерево сверху вниз и останавливайтесь на первом подтверждённом узком месте. В колонке «доказательство» должен быть trace, timing или повторяемый пользовательский симптом. Формулировка «кажется, CDN медленный» не подходит. После исправления повторите исходный путь; если симптом остался, возвращайтесь в дерево, а не добавляйте вторую случайную оптимизацию.

Наблюдение

Что измерить

Следующая ветка

Доказательство

Долго нет первого ответа

Navigation timing, TTFB, Server-Timing, backend trace

DNS/TLS, edge, cache, database или приложение

Одинаковый request ID связывает браузер и сервер

Ответ пришёл, главный блок поздний

LCP element, waterfall, priority и размер ресурса

Изображение, шрифт, CSS, preload или render path

LCP timestamp совпадает с конкретным ресурсом

Нажатие отвечает с задержкой

INP interaction и Performance main thread

Длинная задача, handler, layout или hydration

Trace показывает блокирующий участок вокруг события

Контент скачет

CLS entries и элементы с layout shift

Размер media, поздняя вставка или шрифт

Shift attribution указывает конкретные узлы

Медленно только у части людей

Field p75, сегмент устройства, география и route

Слабый CPU, сеть, вариант страницы или backend shard

Сегмент повторяемо отличается от общего origin

Лаборатория быстрая, жалобы остаются

RUM route, cache state и реальные interactions

Сценарий отсутствует в тесте или long-tail

Полевой event связан с конкретным release и route

Бюджет записывают до оптимизации

Для Core Web Vitals текущие ориентиры good — LCP не более 2,5 секунды, INP не более 200 миллисекунд и CLS не более 0,1 на 75-м процентиле отдельно для mobile и desktop. Это полезная внешняя рамка, но не полный бюджет продукта. Добавьте время серверного действия, вес критического JavaScript, лимит сторонних запросов и продолжительность ключевой операции там, где они действительно влияют. Лимиты для байтов и backend нельзя честно взять из универсальной статьи: зафиксируйте текущий baseline, целевой пользовательский результат и ресурсы устройства.

Показатель

Сегмент

Бюджет

Проверка

LCP

Ключевой route, mobile p75

≤ 2,5 с как внешний ориентир

Field data плюс повторяемый lab trace

INP

Реальные interactions, p75

≤ 200 мс как внешний ориентир

RUM attribution и Performance trace

CLS

Полный жизненный цикл страницы, p75

≤ 0,1 как внешний ориентир

Field metric и layout shift entries

Backend action

Конкретный endpoint и percentile

Командный SLO, не универсальное число

Trace, database timing и Server-Timing

Критический payload

Первый холодный визит

Лимит команды по целевому устройству

Network waterfall и coverage

Ветка 1: сервер и сеть

Откройте Network panel и найдите navigation request. Смотрите не только duration: отделите ожидание соединения, отправку запроса, waiting for server response и загрузку тела. Navigation Timing и Resource Timing дают программные временные точки, но cross-origin ресурсы могут скрывать детали без разрешающего заголовка. Если долго именно ожидание ответа, свяжите браузерный запрос с backend trace. Server-Timing полезен, когда сервер осознанно публикует безопасные агрегаты этапов — например, cache и application — но сам заголовок не измеряет базу за вас.

Проверьте, воспроизводится ли задержка на одном endpoint, только после авторизации, при холодном cache или в конкретном регионе. Не объявляйте базу виноватой по общему TTFB. Один request может ждать внешний API, очередь, блокировку connection pool или сериализацию ответа. Для каждого кандидата нужен собственный timing в trace. Если проблема исчезает при повторном запросе, это не всегда успех: пользователь первого визита всё ещё платит цену прогрева.

Ветка 2: ресурсы и главный контент

Если HTML приходит вовремя, найдите элемент LCP и ресурс, от которого он зависит. Большая картинка может быть проблемой из-за формата, размера или позднего обнаружения; маленькая картинка тоже окажется поздней, если её URL создаётся JavaScript после длинной цепочки. Аналогично шрифт может задерживать текст, а CSS — блокировать отрисовку. Waterfall показывает порядок и приоритеты. Исправление должно сокращать причинный путь: убрать лишнюю зависимость, дать корректный размер, загрузить ресурс вовремя или не делать его критическим.

Не загружайте все изображения с высоким priority и не preloading каждый файл. Приоритет — ограниченный ресурс, а лишний preload конкурирует с действительно важным. Составьте список ресурсов до LCP и спросите про каждый: нужен ли он для первого экрана, когда браузер узнаёт URL, соответствует ли размер месту, можно ли отложить третью сторону. После изменения сравните waterfall в одинаковом cache state.

Ветка 3: main thread и взаимодействие

Медленный отклик после клика диагностируется записью Performance вокруг конкретного interaction. Найдите длинные задачи, обработчик события, style recalculation, layout и paint. Размер JavaScript — лишь косвенный признак: небольшой bundle может выполнять дорогой алгоритм, а большой — не попадать в критический путь. Разбейте работу, удалите ненужную и перенесите вычисление, только если trace подтверждает участок. Для server-rendered интерфейса отдельно проверьте hydration и повторную работу, которую пользователь не просил.

INP — полевая метрика отзывчивости. Lighthouse без реального ввода не измеряет её так же, как пользовательская сессия, и лабораторный TBT служит диагностическим приближением, а не заменой. Сохраняйте attribution по типу interaction и route. Если форма медленна только при валидации большого списка, общий origin-level показатель может скрыть проблему.

Ветка 4: визуальная стабильность

CLS растёт, когда видимые элементы неожиданно меняют положение. Запишите полный сценарий, а не только первые секунды: баннер согласия, поздний ответ API или развёрнутая ошибка тоже могут сдвинуть интерфейс. Ищите layout shift entries и затронутые узлы. Частые причины — media без зарезервированного места, вставка блока над текущим содержимым и замена шрифта с другой геометрией. Исправление — сохранить место, управлять появлением или выбрать метрики шрифта, а не просто скрыть анимацию.

Полевые и лабораторные данные не спорят

Полевой p75 отвечает, насколько хорошо маршрут работает для большинства измеренных посещений в сегменте. Лаборатория отвечает, что происходит в заданном воспроизводимом окружении. PageSpeed может показать CrUX и Lighthouse рядом, но это не две попытки измерить одно и то же посещение. Если поля нет, это может означать недостаток доступных данных, а не идеальную скорость. Если lab зелёный, а поле красное, проверьте реальный route, устройства, версию релиза, third-party и interactions.

После оптимизации не удаляйте доступную структуру ради меньшего DOM и не заменяйте подписи и controls декоративными элементами. Перед выпуском полезно провести ручной аудит доступности: клавиатура, visible focus, подписи и порядок чтения являются частью качества, а не конкурентом скорости.

Пример: медленное сохранение карточки

Предположим, список открывается быстро, но после нажатия «Сохранить» кнопка остаётся заблокированной несколько секунд. Network показывает, что запрос отправлен сразу, а waiting занимает почти всё время. Performance trace не содержит длинной задачи. Backend trace связывает request ID с последовательными обращениями к двум внешним системам. В этом случае сжатие картинок и удаление CSS не исправят симптом. Команда должна решить, можно ли параллелить вызовы, изменить контракт на асинхронный статус или убрать внешнюю зависимость из критического пути, затем проверить ту же карточку.

Если причина повторяется в разных функциях — например, все действия последовательно обращаются к одному медленному сервису или каждый релиз возвращает слишком большой client bundle, — не оставляйте её серией разрозненных тикетов. Стоит занести повторяющуюся причину в карту долга с наблюдаемым последствием, частотой и владельцем. Так оптимизация конкурирует с фичами по пользовательскому влиянию, а не по громкости жалобы.

Ограничения и практический следующий шаг

Ни одна метрика не доказывает ценность продукта. Быстрый экран может вести к запутанному процессу, а выбранная форма клиента — создавать лишние переходы. Если основной путь изначально не соответствует каналу, полезно пересмотреть форму первой версии. Также учитывайте privacy: performance telemetry не должна собирать введённые данные, URL с секретами и персональные payload.

Практический следующий шаг: выберите одну жалобу, запишите route, устройство, сеть, cache state и ожидаемое действие. Снимите три waterfall и один Performance trace, найдите первый подтверждённый медленный слой и назначьте ровно одну проверку. До правки запишите бюджет и способ повторного замера. Если вы не можете связать изменение с исходным симптомом, это ещё не диагностика — вернитесь к дереву.

Что читать дальше