Перейти к содержимому

Performance Metrics: Core Web Vitals

Google Core Web Vitals (LCP, INP, CLS) стали официальными сигналами ранжирования с 2021–2024 гг.; умение объяснить каждую метрику, измерить её в коде и предложить конкретные оптимизации — обязательный навык TechLead в компаниях с серьёзным вниманием к production-перформансу.

// Core Web Vitals: порог Good/Needs Improvement/Poor (75-й перцентиль пользователей)
//
// Метрика Что измеряет Good Poor
// ─────── ──────────────────────────────────── ──────── ──────
// LCP Largest Contentful Paint (загрузка) <= 2.5s > 4s
// INP Interaction to Next Paint (отклик) <= 200ms > 500ms
// CLS Cumulative Layout Shift (стабильность) <= 0.1 > 0.25
// TTFB Time to First Byte (сервер) <= 800ms > 1800ms
// FCP First Contentful Paint <= 1.8s > 3s
//
// FID (First Input Delay) заменён на INP в марте 2024 —
// INP учитывает ВСЕ взаимодействия, не только первое.
// Измерение через web-vitals (Google, ~2 КБ gzip)
import { onLCP, onINP, onCLS, onFCP, onTTFB } from 'web-vitals';
function sendToAnalytics({ name, value, rating, id }) {
navigator.sendBeacon('/analytics', JSON.stringify({ name, value, rating, id }));
}
onLCP(sendToAnalytics);
onINP(sendToAnalytics);
onCLS(sendToAnalytics); // накапливается, отправляется при unload
// PerformanceObserver: низкоуровневый API браузера
const po = new PerformanceObserver(list => {
for (const entry of list.getEntries()) {
console.log(entry.entryType, entry.name, (entry.duration ?? entry.startTime).toFixed(1));
}
});
po.observe({ type: 'largest-contentful-paint', buffered: true });
po.observe({ type: 'layout-shift', buffered: true });
po.observe({ type: 'first-input', buffered: true });
po.observe({ type: 'longtask', buffered: true }); // > 50ms блокирует INP
// Поиск Long Tasks (виновники плохого INP)
new PerformanceObserver(list => {
for (const t of list.getEntries()) {
if (t.duration > 50) {
console.warn(`Long task: ${t.duration.toFixed(0)}ms`, {
startTime: t.startTime,
attribution: t.attribution,
});
}
}
}).observe({ type: 'longtask' });
// Navigation Timing (TTFB, DOMContentLoaded, load)
const [nav] = performance.getEntriesByType('navigation');
console.log('TTFB:', nav.responseStart - nav.requestStart);
console.log('DOMContentLoaded:', nav.domContentLoadedEventEnd - nav.fetchStart);
// Оптимизации под каждую метрику
// LCP: preload hero-image, fetchpriority
// <link rel="preload" as="image" href="/hero.webp" fetchpriority="high">
// <img src="/hero.webp" fetchpriority="high" width="1200" height="600">
// Избегайте: lazy-loading hero image, render-blocking fonts/scripts
// INP: разбивать длинные задачи через scheduler.yield()
async function processLargeList(items) {
const CHUNK = 100;
for (let i = 0; i < items.length; i++) {
process(items[i]);
if (i % CHUNK === 0 && i > 0) {
// Scheduler API (Chrome 115+) или fallback
await (typeof scheduler !== 'undefined' && scheduler.yield
? scheduler.yield()
: new Promise(r => setTimeout(r, 0)));
}
}
}
// CLS: резервировать размеры для медиа (избегать layout shift)
// <img width="800" height="600" src="photo.jpg"> <!-- браузер резервирует место -->
// CSS: aspect-ratio: 16 / 9; /* до загрузки */
// Виновники CLS: font swap, динамические баннеры без min-height, lazy images без размеров

Итог: LCP (загрузка), INP (интерактивность), CLS (стабильность) — метрики пользовательского опыта с прямым влиянием на SEO и конверсию; измеряйте на реальных устройствах (RUM).