Оптимизация 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 — это не «магия» и не разовая настройка, а архитектурная дисциплина. Чем раньше вы закладываете стратегии рендеринга, загрузки шрифтов/медиа и состояния компонентов, тем стабильнее растёт продукт: страницы быстрее, интерфейсы предсказуемее, а метрики — в зелёной зоне. Сроки/стоимость разработки уточняйте по контактам на сайте.