Core Web Vitals — LCP, FID, CLS
Core Web Vitals — набор метрик Google для измерения пользовательского опыта загрузки страниц: LCP (загрузка), INP/FID (интерактивность) и CLS (визуальная стабильность). Влияют на ранжирование в Google Search.
Зачем нужно
Core Web Vitals — стандарт измерения производительности с 2021 года. Плохие значения снижают позиции в Google, увеличивают bounce rate и уменьшают конверсию. Amazon: +100ms задержки → -1% продаж. Google: оптимизация LCP → +20% конверсий.
Где используется
- SEO оптимизация — фактор ранжирования в Google Search
- Мониторинг производительности production приложений
- Performance budget в CI/CD
Основной контент
Три Core Web Vitals
LCP — Largest Contentful Paint (загрузка основного контента)
Good: < 2.5s | Needs Improvement: 2.5-4.0s | Poor: > 4.0s
Что измеряет: время рендеринга наибольшего элемента (hero-image, h1)
INP — Interaction to Next Paint (заменил FID с марта 2024)
Good: < 200ms | Needs Improvement: 200-500ms | Poor: > 500ms
Что измеряет: задержку ответа на все взаимодействия пользователя
CLS — Cumulative Layout Shift (визуальная стабильность)
Good: < 0.1 | Needs Improvement: 0.1-0.25 | Poor: > 0.25
Что измеряет: суммарный сдвиг макета (прыгающие элементы)
Измерение с web-vitals библиотекой
import { onLCP, onINP, onCLS, onFCP, onTTFB } from 'web-vitals';
function sendToAnalytics({ name, value, rating, id }) {
// rating: 'good' | 'needs-improvement' | 'poor'
analytics.track('web_vital', {
metric: name,
value: Math.round(name === 'CLS' ? value * 1000 : value),
rating,
page: location.pathname,
});
}
onLCP(sendToAnalytics);
onINP(sendToAnalytics);
onCLS(sendToAnalytics);
Улучшение LCP
<!-- 1. Preload hero-image -->
<link rel="preload" as="image" href="/hero.avif" fetchpriority="high">
<!-- 2. Атрибут fetchpriority -->
<img src="/hero.avif" fetchpriority="high" alt="Hero" width="1200" height="600">
<!-- 3. Не lazy-load LCP-элемент -->
<!-- ПЛОХО: -->
<img src="/hero.jpg" loading="lazy">
<!-- ХОРОШО: loading="lazy" только для элементов ниже fold -->
Улучшение CLS
/* 1. Всегда задавать размеры изображений */
img { aspect-ratio: 16/9; width: 100%; }
/* 2. Резервировать место для динамического контента */
.ad-slot {
min-height: 250px; /* Резервируем место до загрузки рекламы */
contain: layout; /* CSS Containment */
}
/* 3. Шрифты: size-adjust для предотвращения FOUT */
@font-face {
font-family: 'MyFont';
src: url('myfont.woff2');
size-adjust: 97%; /* Подбирается под системный fallback */
font-display: optional; /* Не показывать FOUT */
}
Улучшение INP
// Разбивать длинные задачи через scheduler
async function heavyOperation(data) {
// Обработка частями с возможностью рендера между ними
for (const chunk of chunks(data, 50)) {
processChunk(chunk);
await scheduler.yield; // Освобождаем main thread
}
}
// Или через setTimeout(0) для совместимости
function yieldToMain() {
return new Promise(resolve => setTimeout(resolve, 0));
}
Частые ошибки
- lazy loading для LCP-элемента (hero image) — откладывает именно то, что нужно загрузить первым
- Изображения без
widthиheight— браузер не знает размер до загрузки → CLS - Вставка элементов выше существующего контента — сдвигает страницу → CLS
- Тяжёлый JavaScript в main thread → долгие tasks → плохой INP
Связанные темы
- _MOC Производительность
- Метрики -- FCP, TTFB, TTI, TBT
- Chrome User Experience Report (CrUX)
- Real User Monitoring (RUM)
- Как браузер рендерит страницу