Слово «медленно» нужно превратить в наблюдение
Жалоба «страница долго открывается» может означать белый экран до ответа, позднее появление главного блока, зависание после нажатия, скачущий 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, найдите первый подтверждённый медленный слой и назначьте ровно одну проверку. До правки запишите бюджет и способ повторного замера. Если вы не можете связать изменение с исходным симптомом, это ещё не диагностика — вернитесь к дереву.
Обсуждение
Комментарии
Обсуждение загрузится при приближении к разделу.