Почему это важно не только для SEO
С 2021 года Google включил Core Web Vitals в алгоритм ранжирования. Но главная причина следить за этими метриками — не позиции в поиске, а реальный пользовательский опыт. Страница, которая прыгает при загрузке или не реагирует на клик полсекунды, теряет пользователей быстрее, чем любой алгоритм успевает её понизить.
Три метрики:
- LCP (Largest Contentful Paint) — когда отрисовывается самый большой элемент на экране
- CLS (Cumulative Layout Shift) — суммарный сдвиг макета во время загрузки
- INP (Interaction to Next Paint) — время от клика или тапа до визуального отклика
Пороги «хорошо» / «плохо»:
- LCP: хорошо < 2.5 с, плохо > 4 с
- CLS: хорошо < 0.1, плохо > 0.25
- INP: хорошо < 200 мс, плохо > 500 мс
LCP — что тормозит главный элемент страницы
LCP-элемент — почти всегда героическое изображение в шапке или крупный заголовок. Посмотреть, что именно считается LCP на конкретной странице, можно в Chrome DevTools: вкладка Performance → Timings → LCP.
Типичные виновники медленного LCP:
- Картинка в шапке загружается поздно — браузер обнаруживает её только при разборе CSS или JS, а не сразу в HTML
- Сервер медленно отвечает (TTFB > 600 мс)
- Изображение не оптимизировано — весит 2 МБ в PNG вместо 80 КБ в WebP
Фикс №1 — fetchpriority
Добавь атрибут fetchpriority="high" на LCP-изображение:
<img src="hero.webp" fetchpriority="high" alt="...">
Это говорит браузеру: не жди, качай это первым. Прирост LCP — обычно 300–600 мс без каких-либо других изменений.
Фикс №2 — preload для фонового изображения
Если LCP-элемент — фон через CSS, браузер вообще не знает о нём до разбора стилей. Добавляй preload в <head>:
<link rel="preload" as="image" href="hero.webp">
Фикс №3 — конвертация в WebP или AVIF
PNG и JPEG в большинстве случаев можно безболезненно заменить на WebP. Потеря качества незаметна, размер падает в 3–5 раз. AVIF ещё эффективнее, но поддержка чуть хуже — используй <picture> с fallback:
<picture>
<source srcset="hero.avif" type="image/avif">
<source srcset="hero.webp" type="image/webp">
<img src="hero.jpg" fetchpriority="high" alt="...">
</picture>
TTFB лечится на уровне сервера или CDN. Если хостинг отвечает за 800 мс — никакие оптимизации фронтенда не спасут. Нужен либо переезд на нормальный VPS, либо CDN поверх.
CLS — почему страница прыгает и как это остановить
CLS — одна из самых раздражающих вещей в интерфейсе. Тянешься нажать кнопку, страница сдвигается, и промахиваешься. Или читаешь текст — и он уезжает вниз, потому что над ним загрузился баннер.
Что вызывает CLS:
- Изображения без явных атрибутов
width и height
- Рекламные блоки и баннеры, которые появляются после загрузки
- Веб-шрифты с FOUT (Flash of Unstyled Text)
- Динамически добавляемый контент над уже отрисованным
Фикс №1 — размеры изображений
Всегда указывай width и height в HTML. Браузер заранее резервирует нужное место:
<img src="photo.webp" width="800" height="600" alt="...">
Без этих атрибутов браузер не знает размер до загрузки. Как только картинка появляется — всё, что ниже, едет вниз.
Фикс №2 — место под рекламу
Если баннер загружается асинхронно, задай контейнеру минимальную высоту через CSS:
.ad-banner {
min-height: 90px;
}
Фикс №3 — шрифты
Используй font-display: optional — браузер использует системный шрифт, если кастомный не загрузился за первые 100 мс. Никакого сдвига:
@font-face {
font-family: 'MyFont';
src: url('font.woff2') format('woff2');
font-display: optional;
}
Либо предзагружай шрифты в <head>:
<link rel="preload" as="font" href="font.woff2" crossorigin>
Реальный кейс: сайт интернет-магазина имел CLS 0.38 — это уже в зоне «плохо». Причина — слайдер в шапке без фиксированной высоты. После добавления aspect-ratio: 16/9 на контейнер и width/height на все изображения слайдера CLS упал до 0.04. Время работы — 20 минут.
INP — самая новая и самая недооценённая метрика
INP заменил FID в марте 2024. Если FID мерял только первое взаимодействие, то INP отслеживает все клики, тапы и нажатия клавиш за сессию и берёт 98-й перцентиль. Порог хорошего значения — 200 мс: от нажатия до визуального отклика.
Что убивает INP:
- Длинные задачи (Long Tasks) в основном потоке — скрипты, которые работают дольше 50 мс
- Тяжёлые обработчики событий: кнопка «Добавить в корзину» синхронно пересчитывает всю корзину до того, как обновить UI
- Избыточные перерисовки DOM после клика
Как искать проблемы:
Chrome DevTools → Performance → запись сессии. Ищешь Long Tasks — красные блоки поверх графика. Задача > 50 мс — потенциальный виновник.
Быстрый способ через консоль:
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.duration > 200) {
console.log('Slow INP:', entry);
}
}
});
observer.observe({ type: 'event', buffered: true });
Как чинить:
Разбивай длинные задачи через scheduler.yield():
async function handleAddToCart(item) {
updateCartUI(item); // быстро — делаем сразу
await scheduler.yield(); // уступаем браузеру
await recalculateTotal(); // тяжёлая операция — после
await saveToServer(item);
}
Пользователь видит реакцию мгновенно. Тяжёлая работа идёт фоном.
Для React — переноси тяжёлые вычисления в startTransition:
import { startTransition } from 'react';
function handleFilter(value) {
startTransition(() => {
setFilteredItems(heavyFilter(items, value));
});
}
Инструменты — чем мерять
Lighthouse — встроен в Chrome DevTools. Запускай в режиме инкогнито, чтобы расширения не искажали результат. Минус: лабораторные данные, не полевые.
PageSpeed Insights — показывает и лабораторные данные, и полевые (CrUX — реальные пользователи с реальных устройств). Смотри именно полевые. Если их нет — сайт слишком мало посещают для репрезентативной выборки.
Search Console → раздел «Основные интернет-показатели» — сводная картина по всему сайту с разбивкой по URL. Именно на эти данные Google ориентируется при ранжировании.
web-vitals — JavaScript-библиотека от Google для сбора метрик прямо в коде:
import { onLCP, onCLS, onINP } from 'web-vitals';
onLCP(console.log);
onCLS(console.log);
onINP(console.log);
Подключи это к своей аналитике — и будешь видеть реальные данные от реальных пользователей, а не только цифры из синтетических тестов.
Порядок работы на реальном проекте
Когда в REEXY приходит задача на производительность, работаем по одной схеме:
- Аудит — PageSpeed Insights плюс Search Console. Смотрим, что именно красное и на каких страницах.
- LCP первым — чаще всего даёт максимальный прирост за минимум времени.
- CLS следом — обычно лечится быстро, если понимаешь причины.
- INP в конце — требует профилирования и занимает больше времени.
Не пытайся оптимизировать всё сразу вслепую. Сначала измерь, потом чини конкретную проблему, потом снова измерь.
Что ещё влияет, но не входит в Core Web Vitals
TTFB формально не Core Web Vital, но напрямую влияет на LCP. Хороший порог — до 200 мс. Если сервер отвечает медленнее — фронтенд-оптимизации не помогут.
Render-blocking ресурсы — CSS и JS, которые блокируют отрисовку. Некритичный JS переноси в конец body или добавляй defer. Некритичный CSS — грузи асинхронно.
Лишние начертания шрифтов — если подключаешь Google Fonts, не тяни все 9 вариантов. Оставь только те, которые реально используются.
Быстрый чек-лист перед деплоем
- LCP-изображение имеет
fetchpriority="high" или preload
- Все изображения имеют атрибуты
width и height
- Изображения в WebP или AVIF
- Шрифты с
font-display: optional или с preload
- Рекламные блоки с зарезервированным местом
- Нет JS-скриптов без
defer или async в <head>
- TTFB сервера < 200 мс
- Long Tasks < 50 мс в критических обработчиках событий
Большинство этих пунктов закрываются за несколько часов работы. Результат — сайт быстрее реагирует, меньше раздражает пользователей и лучше ранжируется в поиске. Не потому что Google сказал «оптимизируй», а потому что это просто более качественный продукт.