Оптимизация LCP, CLS, INP: принципы, паттерны и чек-листы

Core Web Vitals — это не «оценка Google», а набор метрик про реальный опыт: как быстро появляется крупный контент (LCP), насколько «прыгает» верстка (CLS) и как отзывчиво ведут себя интерфейсы (INP). В этом материале — практики, которые мы применяем при разработке сайта под ключ и при редизайне существующих продуктов: стратегии рендеринга, загрузка шрифтов/медиа, управление состояниями компонентов UI, контроль аналитики и чек-листы. Сроки/стоимость разработки уточняйте по контактам на сайте.

1) LCP (Largest Contentful Paint): как показать главное раньше

LCP — крупный элемент выше фолда: фото героя, заголовок, фон-блок с изображением. Цель: уложиться до 2.5 с на мобильных. На практике LCP гасит не «медленный сервер», а цепочка мелочей: неоптимальные шрифты, ленивые критичные изображения, блокирующие скрипты, долгое разрастание CSS.

Паттерны ускорения LCP

  • Сервер и кэш. SSR/SSG для первого экрана, HTTP/2+, короткий TTFB, кэш на краю. Для динамики — стабильные API-времена, статика через CDN.
  • Изображения героя. Используйте оптимизатор картинок, корректный размер контейнера, форматы WebP/AVIF, атрибутыwidth/height и без lazy-load для LCP-изображения.
  • Шрифты. Предзагрузка заголовочного шрифта, font-display: swap или optional, system fallback, подмножества (subset) и вар-шрифты при необходимости.
  • CSS-критика. Минимизируйте критический CSS, не тяните крупные UI-библиотеки для первого экрана. Утяжеляющие стили — по требованию.
  • JS-режим экономии. Компоненты ниже фолда — динамическим импортом; аналитика и виджеты — с отложенным стартом.
  • Приоритеты сети. preload LCP-изображения,preconnect к доменам медиа и шрифтов; не злоупотребляйте лишними preconnect — это тоже запросы.

Отдельный выигрыш — «содержательный» заголовок как LCP. Если визуально первый смысл — крупный H1, сделайте его LCP: текст приходит вместе с HTML и показывает ценность сразу, пока изображение догружается.

2) CLS (Cumulative Layout Shift): как зафиксировать верстку

CLS — сумма непредвиденных сдвигов во время загрузки. Причины: медиа без размеров, веб-шрифты, баннеры/виджеты, вставки над контентом, динамическая высота блоков. Цель — < 0.1.

Паттерны фиксации CLS

  • Резерв под медиа. Всегда задавайте width/height или соотношение сторон (aspect-ratio) для изображений, видео, iframes.
  • Шрифты. Используйте font-display: swap и сопоставимые fallback шрифты (метрики близкие к основному), чтобы замена не «прыгала».
  • Реклама/виджеты. Резервируйте место, не вставляйте «вверх» над уже отрисованным текстом; добавляйте placeholder с фиксированной высотой.
  • Хедер/баннеры. Высота известна заранее; sticky-панели не должны появляться внезапно. Если вводите cookie-бар — не сдвигайте контент.
  • Поздние вставки. Обновляйте контент внутри зарезервированных контейнеров, а не вставляйте новые блоки над текстом.

3) INP (Interaction to Next Paint): отзывчивость действий

INP измеряет «самое медленное» пользовательское действие: клик, тач, клавиатура. Цель — < 200–250 мс для основной массы пользователей. Главные враги: тяжёлые обработчики, блокирующий main-thread, моментальная «гидратация всего» и громоздкие эффектные анимации.

Паттерны снижения INP

  • Разделяйте работу. Крупные вычисления — в web worker; при клике сначала меняйте UI (оптимистичные состояния), тяжёлое — после.
  • Дебаунсы и батчинг. Для ввода и resize — дебаунс; для сетевых кликов — батчинг запросов.
  • Lazy-гидратация. Не гидратируйте весь экран сразу. Компоненты ниже фолда — по пересечению с viewport; вторичные виджеты — по взаимодействию.
  • Мелкий JS. Избегайте больших зависимостей, следите за bundle-splitting, используйте ESM и tree-shaking.
  • Анимации без «стоп-кадра». Анимируйте transform/opacity, избегайте layout-триггеров (width/height/top/left), поддерживайте will-change точечно.

4) Состояния компонентов UI: видимая работа интерфейса

Состояния компонентов UI — основной инструмент контроля перцептивной скорости. Пользователь должен видеть, что происходит: загрузка, успех, ошибка, пусто, ожидание подтверждения. Если UI молчит, клики накапливаются и INP растёт.

Необходимый набор состояний

  • Loading: скелетоны и предзаголовки вместо «пустых полотен».
  • Disabled: кнопка блокируется на время запроса; спиннер — внутри кнопки.
  • Optimistic: мгновенно меняем UI (лайк, добавление в корзину) и догоняем сетью.
  • Empty: контент «что дальше» вместо тишины.
  • Error: человеческий текст ошибки, повторить, канал связи.
  • Focus-visible: контрастные фокусы для клавиатуры и мыши.

Главное — предсказуемость. Кнопка «Оплатить» не может «оживать» спустя секунду без обратной связи. Даже короткая блокировка с индикатором даёт ощущение контроля и снижает «дребезг» кликов.

5) Архитектура загрузки: что, где и когда рендерить

Производительность — это стратегия рендеринга. Условно: сервер рендерит смысл, клиент оживляет детали. Критичное — на сервере, вторичное — позднее или по событию.

  • Критический путь. Текст первого экрана рендерится вместе с HTML, LCP-блок виден сразу, без JS.
  • Композиция модулей. Виджеты, слайдеры, карты, чаты — по lazy-паттерну.
  • Данные по требованию. «Сверху вниз» — минимум запроса; развороты деталей — по клику.
  • Кэш и валидность. Stale-while-revalidate для «почти статических» данных; ручное инвалидационное событие для чувствительных секций.

6) Шрифты и типографика: красиво без «прыжков»

Шрифты — частая причина и LCP-замедлений, и CLS. Базовые правила: один-два семейства, веса по делу, предзагрузка заголовочного, близкий по метрикам fallback (например, для русскоязычных — system UI).

  • font-display: swap или optional, чтобы текст был сразу.
  • Subsetting: вынесите кириллицу и латиницу при необходимости.
  • Variable fonts: один файл вместо пяти — меньше запросов, выше контроль веса.
  • Предзагрузка: только критичных начертаний, не всего семейства.

7) Изображения и видео: вес, размер, первичность

Медиа — самый тяжёлый актив. LCP-изображение — без lazy, остальное — после. Video-постеры с правильным размером; автоплей — только без звука и если не мешает чтению.

  • Используйте современные форматы (WebP/AVIF) и правильные размеры контейнеров.
  • Для галерей/каруселей — lazy + content-visibility за пределами viewport.
  • Серверные трансформации изображений для разных DPR и брейкпоинтов.
  • Плейсхолдеры (blur) — для перцептивной скорости, но не вместо оптимизации.

8) JS-бюджет: сколько кода «можно» на первый экран

Бюджет — это договорённость, а не догма. Часто достаточно 60–100 КБ сжатого JS на первый экран. Важнее дисциплина: каждый импорт — взвешенно, каждый «удобный хелпер» — через линзы бандла.

  • ESM и tree-shaking, без «общих» файлов с побочками.
  • Разделение маршрутов: код страницы не должен тянуть код соседних страниц.
  • Тяжёлые зависимости — по динамическому импорту «только когда нужно».
  • Слежение за регрессом: отчёты бандла и лимиты в CI.

9) Аналитика и измерения: что считаем «зелёной зоной»

Лабораторные замеры полезны, но правит бал полевой трафик (RUM). Отслеживайте LCP/CLS/INP для реальных пользователей с разбивкой по устройствам, странам и шаблонам страниц. В метриках важен не «средний», а перцентиль P75.

  • События рендеринга и ошибок фронта в едином хранилище с дашбордом.
  • Разделение по шаблонам: главная, список, карточка, форма, чек-аут.
  • Алерты на деградацию: падение в зелёную зону, внезапный рост CLS по типу страницы.
  • Корреляция: где INP «стреляет» после релиза — смотрим новые обработчики и анимации.

10) Чек-листы перед релизом

LCP

  • Критический текст в SSR, LCP-изображение без lazy, с preload и размером.
  • Шрифты предзагружены выборочно, есть fallback, font-display настроен.
  • Критический CSS минимален, виджеты/аналитика — отложены.

CLS

  • Все медиа с width/height или aspect-ratio, sticky — с заранее заданной высотой.
  • Cookie/уведомления не сдвигают контент; шрифты не вызывают «скачки».
  • Поздние вставки не ломают поток, место зарезервировано.

INP

  • Нет тяжёлых синхронных обработчиков; интеракции сначала обновляют UI.
  • Lazy-гидратация вне экрана; формам добавлены оптимистичные состояния.
  • Анимации — на transform/opacity, без layout-триггеров.

11) Анти-паттерны

  • Lazy-load LCP-изображения «на всякий случай».
  • Длинные CSS-анимации ширины/высоты на первом экране.
  • Скрытая блокирующая аналитика и виджеты в head.
  • Глобальные CSS, которые тянут стили всех компонентов на любую страницу.
  • Отсутствие состояний компонентов UI — пользователь «долбится» в неотвечающий интерфейс.

Итог простой: оптимизация LCP, CLS, INP — это не «магия» и не разовая настройка, а архитектурная дисциплина. Чем раньше вы закладываете стратегии рендеринга, загрузки шрифтов/медиа и состояния компонентов, тем стабильнее растёт продукт: страницы быстрее, интерфейсы предсказуемее, а метрики — в зелёной зоне. Сроки/стоимость разработки уточняйте по контактам на сайте.