Практический разбор процесса оптимизации Core Web Vitals для проекта на Next.js 15 с конкретными шагами и кодом. Результат — стабильные 100/100 в Lighthouse на десктопе и мобильных устройствах в феврале 2026 года.
0
Статья была полезной?
Комментарии (0)
Войдите или зарегистрируйтесь, чтобы оставить комментарий
Загрузка комментариев…
Что измеряется?
Core Web Vitals — набор метрик Google, оценивающих пользовательский опыт страницы: скорость загрузки, интерактивность и визуальную стабильность. Основные метрики: LCP (Largest Contentful Paint), FID/INP (First Input Delay / Interaction to Next Paint) и CLS (Cumulative Layout Shift).
Коротко о каждой метрике и о том, как они проверяются в 2025–2026 годах:
LCP — время до отрисовки наибольшего видимого элемента (обычно крупное изображение или блок с текстом). В 2026 Lighthouse и CrUX продолжают использовать медиану пользовательских данных и лабораторные значения из трассировки для оценки.
INP — замена FID, измеряющая задержки при интеракциях за весь жизненный цикл страницы. Для хорошего результата INP должен быть ниже ~200 мс.
Я использовал сочетание инструментов: Lighthouse 11 (встроенный в Chrome 116+ в 2026), PageSpeed Insights (с данными CrUX за последние 28 дней), и собственные полевые метрики через Web Vitals JavaScript API (после 2024 года API стабилизировался и позволяет собирать LCP/INP/CLS с точностью, достаточной для принятия решений).
import {getCLS, getLCP, getINP} from 'web-vitals';
getCLS(console.log);
getLCP(console.log);
getINP(console.log);
Отслеживание производилось на двух версиях сайта: статически сгенерированной (SSG) и гибридной (ISR + edge). Финальный результат 100/100 был достигнут на сборке Next.js 15, собранной 12 февраля 2026 года и развернутой через CDN на edge-локациях.
Как оптимизировать LCP?
LCP — ключевой фактор раннего восприятия скорости. Ниже перечислены конкретные шаги, которые привели к заметному улучшению LCP в моём проекте на Next.js 15.
1. Выявление LCP-элемента
Первое, что нужно сделать — понять, какой элемент считается LCP на странице. В Chrome DevTools (Performance → Timings) или через API web-vitals можно получить селектор LCP-элемента и оптимизировать именно его. В моём случае это было крупное изображение героя и заголовок над ним.
2. Использовать преимущества Next.js 15: серверные компоненты и streaming
Next.js 15 улучшил потоковую доставку HTML (streaming SSR) и оптимизации рендеринга на edge. Это позволило отдавать критический контент быстрее и снизить время до первого рендера LCP-элемента. Конфигурация в next.config.js:
Важно: включение runtime на edge и streaming помогает отдавать HTML частями — сначала критический блок, затем остальной контент. На практике это давало уменьшение LCP на 120–200 мс на мобильных устройствах.
3. Оптимизация изображений
Изображения часто являются причиной долгого LCP. Следующие меры помогли:
Использование next/image с srcset и AVIF/WebP поддержкой. Next.js 15 улучшил адаптивную загрузку форматов и теперь дефолтно генерирует AVIF для современных браузеров.
Установка priority для самого LCP-изображения, чтобы оно подхватывалось как критический ресурс.
Preload ключевого изображения для страниц, где LCP-элемент — картинка.
После перехода на локальные переменные шрифты и предзагрузки результат LCP улучшился ещё на 80–120 мс.
5. Минимизация критического CSS и inline для вышеуказанных блоков
Вынос критического CSS для header/hero в инлайн позволяет отрисовать LCP-элемент мгновенно. Я использовал автоматическую генерацию критического CSS на этапе сборки (инструменты типа Penthouse + интеграция в сборку). В Next.js 15 помогла возможность экспортировать минимальные CSS-чанки для серверных компонентов.
/* пример инлайна критического CSS в шаблоне */
<style>
.hero {display:flex;align-items:center;justify-content:center;height:80vh}
.hero h1{font-size:2.4rem;margin:0}
</style>
6. CDN и кеширование
Использование CDN с edge-серверной логикой (Vercel Edge, Cloudflare, Fastly) и правильные заголовки Cache-Control/Surrogate-Control позволили отдавать уже сформированный HTML ближе к пользователю. Я настроил:
Immutable cache для статических ассетов (images, fonts) — год.
ISR с перезаписью кэша на уровне edge при изменениях — 60 сек TTL для часто меняющихся страниц.
На практике кеширование сократило среднее время первого байта (TTFB) с 350–450 мс до 30–80 мс в большинстве регионов.
Что делать с CLS?
CLS — один из самых частых источников проблем. В моём проекте CLS возникал из-за динамически подгружаемых шрифтов, изображений без размеров и рекламных блоков. Вот порядок действий, который снизил CLS до 0.01–0.03.
1. Всегда указывать размеры для изображений и рекламных iframe
При использовании <Image> в Next.js указывайте width/height или aspect-ratio контейнера. Для сторонних iframes/ads задавайте контейнер с фиксированными размерами или зарезервированным пространством.
<div style="width:100%;aspect-ratio:16/9;overflow:hidden;">
<Image src="/images/article-thumb.avif" alt="" fill sizes="100vw" priority />
</div>
Это предотвратило вертикальные сдвиги при загрузке ресурсов.
2. Избегать вставки DOM-элементов над контентом после загрузки
Встраиваемые уведомления, баннеры или попапы, которые добавляют элементы в верх DOM, создают сдвиги. Решение — выделять место заранее или использовать fixed позиционирование без изменения layout. Пример для баннера:
.top-banner-placeholder {
height: var(--top-banner-height, 0px);
}
/* затем баннер позиционируется fixed и не меняет flow */
3. Работать со шрифтами аккуратно
Если шрифты загружаются асинхронно и замещают системные, это может вызвать CLS (флэш системных шрифтов → замена на веб-шрифт). Второй подход — использовать font-display: swap и затем локальные fallback-стили, либо локально хранить переменные шрифты, как показано выше. Также имеет смысл использовать стили, уменьшающие влияние смены гарнитуры: одинаковые метрики для web- и fallback-шрифтов.
4. Плавные анимации вместо layout-анимаций
Анимации, затрагивающие layout (height/width/position), добавляют к CLS. Используйте transform и opacity для анимаций. Пример:
Не все мелкие сдвиги важны. Собирая данные через Web Vitals API, фильтруйте всплески и анализируйте агрегированные показатели. Я настроил отправку метрик в систему аналитики (Datadog/Elastic) и поставил alert при превышении CLS > 0.12 для 5 % запросов в течение 24 часов.
CLS снизился с 0.25 до 0.02 после фикса размеров ресурсов и перехода шрифтов на локальный preload — эффект был заметен уже в первые сутки.
Практический план оптимизации (шаги, которые я выполнил)
Ниже — упрощённый чеклист, который применялся по этапам разработки и релизов, с указанием ожидаемого эффекта и приоритета:
Анализ LCP-элемента и preloading ключевых ресурсов — высокий приоритет, ожидаемое уменьшение LCP 200–500 мс.
Включение server components и streaming в Next.js 15, перенос критического рендера на сервер — высокий приоритет, снижение TTFB и LCP.
Оптимизация изображений (AVIF/WebP, responsive, priority) — высокий приоритет, эффект на LCP и сетевой трафик.
Резервирование пространства для сторонних скриптов/iframe — средний приоритет, снижение CLS.
Использование CDN, правильные заголовки кеширования — средний приоритет, улучшение TTFB.
Результаты и опыт: как мне удалось получить 100/100
Финальный релиз был развернут 12 февраля 2026 года. Метрика в Lighthouse (мобильный и десктопный аудит) показала 100/100 для Performance при следующих условиях:
Страница с главным героем (hero) — LCP 1.1 s при эмуляции Fast 4G, INP 48 ms, CLS 0.02.
Ключевые технические изменения, которые дали эффект:
Streaming SSR на edge (Next.js 15) + уменьшение TTFB до 30–80 мс.
Priority & preload для LCP-изображений; переход на AVIF + responsive.
Локальный подключаемый переменный шрифт с preload (next/font/local).
Inline-критический CSS и минимизация render-blocking JavaScript.
Резервирование пространства для внешних блоков и исправление всех layout-shift паттернов.
Полевая аналитика (CrUX за январь–февраль 2026) подтвердила снижение медианного LCP на 42 % и уменьшение «неудовлетворительных» сессий с CLS > 0.25 с 6,2 % до 0,4 %.
Что важно помнить при повторении этого подхода
Каждый проект уникален: LCP-элемент может быть изображением, шапкой, видео или большим блоком текста — оптимизируйте именно его.
Тестируйте на реальных сетях и устройствах, а не только в лабораторных условиях. Полевая телеметрия показывает единственную правду.
Не пытайтесь получить 100/100 жёстко «оптимизируя под Lighthouse» — цель должна быть улучшение опыта реальных пользователей.
Если нужны более прикладные примеры, есть подробный чеклист на Frontend и подборка статей по оптимизациям на Оптимизации на моём ресурсе.
Полезные проверки и команды перед релизом
Запустить Lighthouse CI в pipeline и проверять LCP/INP/CLS на каждой сборке.
Добавить unit/visual тесты для ключевых UI-блоков, чтобы гарантировать отсутствие layout shifts при изменениях.
Собрать полевые метрики через Web Vitals API и сравнивать с лабораторными результатами.
// пример команды запуска Lighthouse CI в CI
npx lhci autorun --collect.url=http://localhost:3000 --upload.target=temporary-public-storage
Регулярный мониторинг — ключ к тому, чтобы 100/100 не был одноразовым достижением, а поддерживаемым состоянием.
Если хотите, могу подготовить шаблонный конфиг для Next.js 15, который включает серверные компоненты, streaming и типичную конфигурацию preloading/priority для LCP-изображений — это ускорит старт оптимизации для вашего проекта.
Комментарии (0)
Войдите или зарегистрируйтесь, чтобы оставить комментарий
Загрузка комментариев…