Гигиенический минимум по критическим метрикам. Почему сайт тормозит и как это измерить

Frontend

Вы подняли проект с нуля, выполнили все бизнес-требования и запустили в формате MVP, всё работает, но бизнес или пользователи жалуются, что сайт подтупливает. Вы открываете прод на своем MacBook Pro на последнем М-процессоре — всё летает. Где же проблема? Чтобы разобраться, пригодятся знания о Core Web Vitals — трёх главных метриках, которые использует в том числе и Google для оценки скорости и удобства вашего сайта.

Что такое Core Web Vitals?

Core Web Vitals — это три метрики, которые Google считает самыми важными для пользовательского опыта:

  1. LCP (Largest Contentful Paint) — скорость загрузки основного контента.
  2. FID (First Input Delay) — отзывчивость на первое взаимодействие.
  3. CLS (Cumulative Layout Shift) — визуальная стабильность.

С июля 2021 года эти метрики влияют на SEO — сайты с плохими показателями ранжируются ниже в поисковой выдаче Google.

На примере шаурмы

Снова представим ларек с шаурмой, владелец которого хочет увеличить эффективность бизнеса и для этого, начитавшись умных книжек и блогов, решает отслеживать Core Web Vitals.

В таком случае:

  • LCP (Largest Contentful Paint) — время, за которое на стол попадает главное блюдо из заказа. Если его долго не несут — посетитель нервничает.
  • FID (First Input Delay) — время от того, как посетитель попросил добавить в заказ лимонад, до того, как работник потянулся к холодильнику. Если он завис — посетитель напрягается.
  • CLS (Cumulative Layout Shift) — насколько стабилен стол. Если посетитель тянется за купленным лимонадом, а стол внезапно наклоняется, он может пролить напиток и поставить низкую оценку заведению.

LCP: Скорость загрузки основного контента

LCP — время от начала загрузки страницы до момента, когда отобразился самый большой элемент в области просмотра (видимой части экрана).

Что считается «самым большим элементом»?

  • Изображение (<img>)
  • Видео-превью (<video>)
  • Элемент с фоновым изображением (через CSS background-image)
  • Блочный элемент с текстом (<div><p>)

Хороший, плохой, критический

ПоказательОценка
≤ 2.5 секунды🟢 Хорошо
2.5 — 4 секунды🟡 Требует улучшения
> 4 секунды🔴 Плохо

Почему LCP важен?

Пользователь не ждет, пока загрузится вся страница. Ему нужно увидеть основное содержание (заголовок, картинку, кнопку «Купить»). Если этого нет через 2-3 секунды — он склонен уйти.

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

1. Оптимизируйте изображения
<!-- Используйте современные форматы -->
<picture>
    <source srcset="image.avif" type="image/avif">
    <source srcset="image.webp" type="image/webp">
    <img src="image.jpg" alt="Описание">
</picture>
  • Используйте WebP или AVIF (они легче JPG/PNG на 30-50%)
  • Сжимайте изображения (TinyPNG, Squoosh)
2. Удалите блокирующие скрипты
<!-- Скрипты, которые не нужны для первого экрана, загружайте асинхронно -->
<script src="analytics.js" async></script>
<script src="heavy-widget.js" defer></script>
  • async — скрипт загружается параллельно, выполняется сразу
  • defer — скрипт загружается параллельно, выполняется после загрузки HTML
3. Используйте CDN

Раздавайте статику (картинки, CSS, JS) через CDN. Файлы будут ближе к пользователю.

4. Включите кэширование
Cache-Control: max-age=31536000, immutable

Статические файлы кэшируются на год. Пользователь загружает их один раз.

5. Перенесите CSS-критические стили в <head>

<head>
    <style>
        /* Только стили для первого экрана */
        .header { background: #fff; }
        .hero { font-size: 2rem; }
    </style>
    <!-- Остальные стили загружаем асинхронно -->
    <link rel="preload" href="styles.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
</head>

FID: Отзывчивость на первое взаимодействие

FID — время от момента первого взаимодействия пользователя со страницей (клик, нажатие клавиши, касание) до момента, когда браузер начинает обрабатывать это событие.

Почему мы измеряем FID, а не TTI (Time to Interactive)?

Потому что пользователь может начать взаимодействовать до того, как страница полностью загрузилась. FID измеряет задержку именно для первого взаимодействия.

Хороший, плохой, критический

ПоказательОценка
≤ 100 мс🟢 Хорошо
100 — 300 мс🟡 Требует улучшения
> 300 мс🔴 Плохо

Почему FID важен?

Представьте: вы кликаете на кнопку «Добавить в корзину», а она не реагирует 3 секунды. Вы кликаете еще раз, потом еще, потом не перепроверив корзину оформляете заказ. Итог: лишние клики, лишние товары в заказе, а вы в «восторге» от предстоящего общения со службой поддержки.

Как улучшить FID?

1. Разбивайте длинные задачи

Если у вас есть скрипт, который выполняется 200+ мс, он блокирует основной поток. Браузер не может реагировать на клики.

// Плохо — 300 мс блокировки
function heavyCalculation() {
    for (let i = 0; i < 1000000000; i++) {
        // тяжелые вычисления
    }
}

// Хорошо — разбиваем на мелкие части
function heavyCalculation() {
    let i = 0;
    const chunkSize = 1000;
    const results = [];
    
    function chunk() {
		const nextChunk = data.slice(i, i + chunkSize);
		if (nextChunk.length === 0) {
			return results;
		}
		
		// Обработка текущего чанка
		for (const item of nextChunk) {
			results.push(
				// Здесь выполняем необходимые операции над item
			);
		}
		
		i += chunkSize;
		
		// Передаем управление браузеру
		setTimeout(chunk, 0);
    }
    
    chunk();
}

Используйте setTimeoutrequestIdleCallback или Web Workers для тяжелых задач.

2. Не загружайте тяжелый JS на первый экран
// Загружаем тяжелый виджет только после того, как страница стала интерактивной
window.addEventListener('load', () => {
    import('./heavy-widget.js').then(module => {
        module.init();
    });
});
3. Удалите ненужные полифиллы

Для старых браузеров используйте полифиллы. Но не тащите их в те браузеры, которые уже поддерживают необходимую функциональность.

// Загружаем полифилл только если он нужен
if (!('IntersectionObserver' in window)) {
    import('intersection-observer');
}

CLS: Визуальная стабильность

CLS — суммарный сдвиг макета. Измеряет, насколько «прыгает» контент на странице в процессе загрузки.

Как измеряется CLS?

Браузер отслеживает каждый раз, когда видимый элемент меняет свою позицию. Чем больше сдвиг и чем больше элемент — тем выше «штраф».

Хороший, плохой, критический

ПоказательОценка
≤ 0.1🟢 Хорошо
0.1 — 0.25🟡 Требует улучшения
> 0.25🔴 Плохо

Почему CLS важен?

Вы читаете статью, внезапно страница дергается, вы теряете строчку, на которую смотрели. Или вы хотите нажать на кнопку, а она «убегает» вниз — вы кликаете на рекламу вместо кнопки.

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

1. Всегда указывайте размеры изображений и видео
<!-- Плохо — картинка подпрыгнет, когда загрузится -->
<img src="image.jpg" alt="Котик">
<!-- Хорошо — браузер знает, сколько места оставить -->
<img src="image.jpg" alt="Котик" width="800" height="600">
2. Зарезервируйте место под рекламу и iframe
<!-- Если не знаете точную высоту, зарезервируйте минимум -->
<div style="min-height: 250px;">...</div>
3. Не вставляйте контент сверху уже загруженного

Если вы динамически добавляете элементы в начало страницы (например, баннер о куках или pop-up), используйте position: fixed или position: sticky.

/* Баннер, который не сдвигает контент */
.cookie-banner {
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
}
4. Используйте CSS-свойство aspect-ratio
.image-container {
    aspect-ratio: 16 / 9;
    background: #f0f0f0; /* скелетон */
}

Браузер зарезервирует место еще до загрузки картинки.

5. Не используйте шрифты с изменяемой высотой

Некоторые шрифты при загрузке меняют высоту строк, что вызывает сдвиги. Используйте font-display: optional или font-display: swap.

@font-face {
    font-family: 'MyFont';
    src: url('myfont.woff2') format('woff2');
    font-display: swap; /* Пока шрифт не загрузился, покажем системный */
}

Как измерять Core Web Vitals?

Инструменты для измерения

ИнструментДля чего
Google PageSpeed InsightsАнализ страницы + рекомендации
Lighthouse (в DevTools)Локальный аудит
Chrome DevTools (вкладка Производительность)Детальный профилинг
Web Vitals (библиотека)Сбор метрик в реальном времени
Google Search ConsoleCore Web Vitals для всех страниц сайта

Измеряем в реальном времени

// Устанавливаем библиотеку
import { getLCP, getFID, getCLS } from 'web-vitals';

// Отправляем в аналитику
function sendToAnalytics({ name, value }) {
    console.log(`${name}: ${value}`);
    // Отправка в Google Analytics / Яндекс.Метрику
}

getLCP(sendToAnalytics);
getFID(sendToAnalytics);
getCLS(sendToAnalytics);

Что можно сделать для улучшения Core Web Vitals прямо сейчас

LCP

  • Изображения в WebP/AVIF
  • Сжатие изображений
  • Асинхронная загрузка скриптов
  • CDN и кэширование
  • Критический CSS в <head>

FID

  • Разбивка длинных задач
  • Ленивая загрузка JS
  • Минификация JS
  • Удаление полифиллов для современных браузеров

CLS

  • Указаны размеры всех изображений
  • Зарезервировано место под рекламу
  • Используется aspect-ratio
  • font-display: swap
  • Динамический контент добавляется стабильно

Итоги

Пользователь не прощает долгой загрузки. Каждые 100 мс задержки снижают конверсию на 1%. Каждые 2 секунды — на 10%. Оптимизируйте Core Web Vitals — и пользователи скажут вам спасибо. Желаю успехов!

Симо Мофин
Симо Мофин

Senior Frontend Developer
Главный по блогу