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

Связанные темы

Ресурсы