Web Vitals

Web Vitals — набор метрик Google для измерения пользовательского опыта: скорость загрузки, интерактивность и визуальная стабильность.

Зачем нужно

Google использует Web Vitals как фактор ранжирования. Но важнее другое: эти метрики напрямую влияют на конверсию. Каждая дополнительная секунда загрузки снижает конверсию. Web Vitals дают конкретные цифры для оптимизации.

Где используется

Все публичные веб-сайты. Измеряются через Lighthouse, PageSpeed Insights, Chrome DevTools, CrUX (Chrome User Experience Report), Google Search Console.

Предпосылки

HTML/CSS/JavaScript, базовое понимание HTTP

Core Web Vitals (3 главные метрики)

LCP — Largest Contentful Paint

Скорость загрузки. Время до отрисовки самого большого видимого элемента (изображение, заголовок, видео).

Хорошо:    ≤ 2.5 секунды
Требует улучшения: 2.5–4.0 секунды
Плохо:     > 4.0 секунды

Что считается LCP-элементом:

  • <img>
  • <video> (poster)
  • Элемент с background-image
  • Блочный элемент с текстом
// Измерение LCP через Performance API
new PerformanceObserver((entryList) => {
  const entries = entryList.getEntries;
  const lastEntry = entries[entries.length - 1];
  console.log('LCP:', lastEntry.startTime, 'ms');
  console.log('LCP element:', lastEntry.element);
}).observe({ type: 'largest-contentful-paint', buffered: true });

Как улучшить LCP:

  • Оптимизировать изображения (WebP, AVIF, srcset)
  • Предзагрузка LCP-ресурса: <link rel="preload">
  • SSR или Static Generation вместо CSR
  • CDN для статики
  • Убрать render-blocking CSS/JS

INP — Interaction to Next Paint

Отзывчивость. Задержка между действием пользователя (клик, нажатие) и визуальным откликом. Заменил FID в 2024.

Хорошо:    ≤ 200 мс
Требует улучшения: 200–500 мс
Плохо:     > 500 мс
// Измерение INP
new PerformanceObserver((list) => {
  for (const entry of list.getEntries) {
    if (entry.interactionId) {
      console.log('Interaction:', entry.name, entry.duration, 'ms');
    }
  }
}).observe({ type: 'event', buffered: true, durationThreshold: 16 });

Как улучшить INP:

  • Разбить долгие задачи на маленькие (setTimeout, requestIdleCallback)
  • Минимизировать JavaScript на главном потоке
  • Использовать Web Workers для тяжёлых вычислений
  • content-visibility: auto для невидимого контента

CLS — Cumulative Layout Shift

Визуальная стабильность. Сумма всех неожиданных сдвигов элементов на странице.

Хорошо:    ≤ 0.1
Требует улучшения: 0.1–0.25
Плохо:     > 0.25
// Измерение CLS
let clsValue = 0;
new PerformanceObserver((list) => {
  for (const entry of list.getEntries) {
    if (!entry.hadRecentInput) {
      clsValue += entry.value;
      console.log('CLS:', clsValue);
    }
  }
}).observe({ type: 'layout-shift', buffered: true });

Как улучшить CLS:

  • Задать width и height для изображений
  • Использовать aspect-ratio CSS
  • Резервировать место для динамического контента
  • Не вставлять контент выше текущего viewport
  • font-display: swap для шрифтов
<!-- ПЛОХО: изображение без размеров — CLS при загрузке -->
<img src="photo.jpg">

<!-- ХОРОШО: размеры заданы — место зарезервировано -->
<img src="photo.jpg" width="800" height="600" loading="lazy">

<!-- ХОРОШО: через aspect-ratio -->
<img src="photo.jpg" style="aspect-ratio: 4/3; width: 100%;">

Дополнительные метрики

TTFB — Time to First Byte

Время от запроса до первого байта ответа. Отражает скорость сервера.

Хорошо: ≤ 800 мс

FCP — First Contentful Paint

Время до первого отрисованного контента (текст, изображение, SVG).

Хорошо: ≤ 1.8 секунды

Field Data vs Lab Data

Field Data Lab Data
Источник Реальные пользователи Синтетический тест
Инструменты CrUX, Google Search Console Lighthouse, WebPageTest
Отражает Реальный UX Потенциальную проблему
INP Да Нет (только TBT)
Вариативность Высокая (разные устройства) Стабильная

Измерение Web Vitals

web-vitals library

import { onLCP, onINP, onCLS, onFCP, onTTFB } from 'web-vitals';

function sendToAnalytics(metric) {
  console.log(metric.name, metric.value, metric.rating);
  // Отправка в аналитику
  navigator.sendBeacon('/analytics', JSON.stringify(metric));
}

onLCP(sendToAnalytics);
onINP(sendToAnalytics);
onCLS(sendToAnalytics);
onFCP(sendToAnalytics);
onTTFB(sendToAnalytics);

Chrome DevTools

1. F12 → Performance → Record → Выполни действия → Stop
2. Смотри: LCP, CLS, Long Tasks
3. Lighthouse tab → Generate Report

PageSpeed Insights

https://pagespeed.web.dev/
Показывает и Lab Data (Lighthouse), и Field Data (CrUX)

Целевые значения

Метрика Хорошо Средне Плохо
LCP ≤ 2.5с 2.5–4.0с > 4.0с
INP ≤ 200мс 200–500мс > 500мс
CLS ≤ 0.1 0.1–0.25 > 0.25
FCP ≤ 1.8с 1.8–3.0с > 3.0с
TTFB ≤ 800мс 800–1800мс > 1800мс

Частые ошибки

1. Оптимизация только Lab Data

Lighthouse показывает 100, но реальные пользователи жалуются.
Field Data учитывает медленные устройства и соединения.
Проверяй CrUX / Google Search Console.

2. Игнорирование CLS

<!-- Реклама, баннеры, lazy-loaded изображения без размеров
     вызывают сдвиги. Всегда резервируй место! -->
<div style="min-height: 250px;">
  <!-- Рекламный баннер загрузится сюда -->
</div>

3. Тяжёлый JavaScript блокирует INP

// ПЛОХО: долгая задача блокирует интерактивность
button.addEventListener('click', () => {
  for (let i = 0; i < 10000000; i++) { /* тяжёлое вычисление */ }
  updateUI;
});

// ХОРОШО: разбиваем на куски
button.addEventListener('click', async () => {
  await new Promise(r => setTimeout(r, 0)); // Уступаем главный поток
  const result = await computeInChunks(data);
  updateUI(result);
});

Практика

  1. Установи web-vitals и выведи метрики в консоль на своём сайте
  2. Проверь свой сайт в PageSpeed Insights — запиши все метрики
  3. Найди LCP-элемент на главной странице и оптимизируй его загрузку
  4. Добавь width/height ко всем изображениям для улучшения CLS
  5. Проверь Field Data в Google Search Console

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

Ресурсы