Toate ghidurile

Performanța web în practică: Core Web Vitals

Cum măsori și optimizezi LCP, INP și CLS: date de laborator vs. date de teren, imagini, long tasks, fonturi și bugete de performanță.


Un site lent nu pierde doar vizitatori nerăbdători: îi frustrează pe toți cei care au o conexiune modestă sau un telefon mai vechi. Google a standardizat felul în care măsurăm experiența reală a utilizatorilor prin Core Web Vitals, trei metrici care spun cât de repede apare conținutul, cât de repede răspunde pagina și cât de stabilă rămâne. Ghidul acesta explică fiecare metrică, cum o măsori corect și ce optimizări chiar o mișcă.

Core Web Vitals în 2026: LCP, INP și CLS

Setul actual de Core Web Vitals are trei metrici. Interaction to Next Paint (INP) a înlocuit First Input Delay (FID) în martie 2024, așa că orice material care încă vorbește despre FID este depășit.

MetricăCe măsoarăBunNecesită îmbunătățiriSlab
LCP (Largest Contentful Paint)Când apare pe ecran cel mai mare element de conținut (imagine, video sau bloc de text)≤ 2,5 s2,5–4 s> 4 s
INP (Interaction to Next Paint)Cât durează până când pagina afișează reacția la click-uri, atingeri și taste, de-a lungul întregii vizite≤ 200 ms200–500 ms> 500 ms
CLS (Cumulative Layout Shift)Cât de mult „sar” elementele pe pagină în mod neașteptat≤ 0,10,1–0,25> 0,25

Un detaliu important: pragurile se evaluează la percentila 75 a vizitelor, separat pentru mobil și desktop. Nu e suficient ca pagina să fie rapidă pe laptopul tău. Trebuie să fie rapidă pentru cel puțin trei sferturi dintre vizitatori, inclusiv pentru cei cu telefoane modeste și rețele mobile.

Definițiile complete și pragurile oficiale sunt pe web.dev/articles/vitals.

Date de laborator vs. date de teren

Înainte să optimizezi ceva, trebuie să înțelegi de unde vin cifrele.

  • Date de laborator (lab data): o simulare controlată, pe un singur dispozitiv și o singură rețea. Exemple: Lighthouse, panoul Performance din Chrome DevTools. Sunt excelente pentru depanare, pentru că poți reproduce problema și compara variante.
  • Date de teren (field data sau RUM, Real User Monitoring): măsurători de la vizitatori reali. Exemple: Chrome User Experience Report (CrUX), raportul Core Web Vitals din Google Search Console sau propria colectare cu biblioteca web-vitals.

Cele două pot diferi mult. Un caz tipic: Lighthouse nu poate măsura INP, pentru că nu face interacțiuni reale cu pagina; folosește în schimb Total Blocking Time (TBT) ca indicator aproximativ. Când datele de laborator și cele de teren se contrazic, datele de teren descriu experiența reală.

PageSpeed Insights le pune pe amândouă pe aceeași pagină: date CrUX (dacă site-ul are suficient trafic) și un audit Lighthouse.

Colectează propriile date de teren

Biblioteca web-vitals, întreținută de echipa Chrome, raportează metricile exact cum le definește specificația. Le poți trimite la propriul endpoint de analytics:

import { onLCP, onINP, onCLS } from "web-vitals";

function trimiteMetrica(metrica) {
  const date = JSON.stringify({
    nume: metrica.name,
    valoare: metrica.value,
    rating: metrica.rating, // "good", "needs-improvement" sau "poor"
    id: metrica.id,
    pagina: location.pathname,
  });
  // sendBeacon funcționează și când utilizatorul închide pagina
  navigator.sendBeacon("/api/vitals", date);
}

onLCP(trimiteMetrica);
onINP(trimiteMetrica);
onCLS(trimiteMetrica);

Cu aceste date poți vedea exact care pagini și care tipuri de dispozitive au probleme, în loc să ghicești.

Optimizarea LCP

LCP se poate împărți în patru componente, iar fiecare are propriile soluții:

  1. Time to First Byte (TTFB): cât durează până sosește primul byte de HTML.
  2. Întârzierea până la descărcarea resursei: cât timp trece până când browserul începe să descarce imaginea LCP.
  3. Durata descărcării resursei: cât durează descărcarea propriu-zisă.
  4. Întârzierea randării: timpul dintre sosirea resursei și afișarea ei pe ecran.

Ce poți face concret

  • Redu TTFB: servește paginile dintr-un CDN, folosește caching pe server, generează static (SSG) paginile care nu se schimbă la fiecare cerere.
  • Fă imaginea LCP descoperibilă imediat: pune-o direct în HTML, nu o încărca din JavaScript și nu o ascunde într-un background-image din CSS, unde browserul o găsește târziu.
  • Nu aplica lazy loading imaginii LCP: loading="lazy" e util pentru imaginile de mai jos în pagină, dar pentru imaginea principală întârzie descărcarea.
  • Dă-i prioritate: atributul fetchpriority="high" îi spune browserului că imaginea e importantă.
  • Micșorează fișierul: formate moderne (AVIF, WebP), dimensiuni adecvate pentru fiecare ecran prin srcset și sizes.
  • Elimină resursele care blochează randarea: CSS critic inline sau minim, scripturi cu defer sau type="module".
<img
  src="/hero-1200.avif"
  srcset="/hero-800.avif 800w, /hero-1200.avif 1200w, /hero-1800.avif 1800w"
  sizes="100vw"
  width="1200"
  height="600"
  fetchpriority="high"
  alt="Echipa noastră lucrând la un proiect"
/>

Dacă imaginea LCP e totuși definită în CSS, o poți anunța devreme cu un preload: <link rel="preload" as="image" href="/hero.avif" fetchpriority="high">.

Framework-uri precum Next.js au componente de imagine care generează automat srcset, dimensiuni și formate moderne. Merită folosite, dar tot tu trebuie să marchezi care imagine e cea prioritară.

Optimizarea INP

INP măsoară latența interacțiunilor de-a lungul întregii vizite și raportează, în mare, una dintre cele mai lente. Latența unei interacțiuni are trei părți:

  • Input delay: browserul nu poate procesa click-ul pentru că firul principal (main thread) e ocupat cu altceva.
  • Processing time: rulează handler-ele tale de evenimente.
  • Presentation delay: browserul recalculează stilurile și layout-ul și desenează noul cadru.

Cauza cea mai frecventă a unui INP slab sunt long tasks, adică sarcini JavaScript de peste 50 ms care blochează firul principal. Cât timp rulează o astfel de sarcină, pagina nu poate răspunde la nimic.

Sparge sarcinile lungi

Dacă trebuie să procesezi mult, cedează periodic controlul înapoi browserului, ca să poată trata interacțiunile între bucăți:

function cedeazaFirul() {
  if (globalThis.scheduler?.yield) {
    return scheduler.yield();
  }
  return new Promise((resolve) => setTimeout(resolve, 0));
}

async function proceseazaElemente(elemente) {
  let ultimaCedare = performance.now();

  for (const element of elemente) {
    proceseaza(element);

    // Cedează firul principal cam la fiecare 50 ms de lucru
    if (performance.now() - ultimaCedare > 50) {
      await cedeazaFirul();
      ultimaCedare = performance.now();
    }
  }
}

Codul folosește scheduler.yield() acolo unde browserul îl oferă și revine la setTimeout în rest.

Alte optimizări pentru INP

  • Oferă feedback vizual imediat: actualizează interfața (un spinner, o stare „se salvează”) înainte să pornești munca grea.
  • Livrează mai puțin JavaScript: code splitting, eliminarea dependențelor inutile, încărcarea scripturilor terțe (chat, analytics, reclame) cu prioritate redusă.
  • Mută calculele grele într-un Web Worker, unde nu blochează firul principal.
  • Redu dimensiunea DOM-ului: un DOM foarte mare face fiecare recalculare de stil și layout mai scumpă. Proprietatea CSS content-visibility: auto ajută la paginile lungi.
  • Evită layout thrashing: nu alterna citiri (offsetHeight) și scrieri de stil în aceeași buclă.
  • În React, marchează actualizările care nu sunt urgente cu useTransition sau useDeferredValue, ca interfața să rămână receptivă.

Optimizarea CLS

CLS crește atunci când elemente deja vizibile își schimbă poziția fără ca utilizatorul să fi cerut asta. Exemplul clasic: vrei să apeși un buton, o reclamă se încarcă deasupra lui și apeși pe altceva. Deplasările care apar imediat după o interacțiune a utilizatorului (de exemplu, deschiderea unui meniu) nu se pun la socoteală.

Cauzele frecvente și soluțiile lor:

CauzăSoluție
Imagini și video fără dimensiuniAtributele width și height sau proprietatea CSS aspect-ratio
Reclame, embed-uri, iframe-uriRezervă spațiu fix pentru container înainte să se încarce
Bannere injectate deasupra conținutuluiAfișează-le ca overlay sau rezervă-le locul din start
Fonturi web care înlocuiesc fontul de rezervăfont-display și un font de rezervă cu metrici ajustate
Animații pe top, left, widthAnimații cu transform și opacity
/* Spațiu rezervat pentru un video încorporat */
.video-embed {
  width: 100%;
  aspect-ratio: 16 / 9;
}

/* Fontul principal */
@font-face {
  font-family: "Inter";
  src: url("/fonts/inter.woff2") format("woff2");
  font-display: swap;
}

/* Font de rezervă scalat ca să ocupe aproximativ același spațiu */
@font-face {
  font-family: "Inter Fallback";
  src: local("Arial");
  size-adjust: 107%;
}

body {
  font-family: "Inter", "Inter Fallback", sans-serif;
}

Valoarea exactă pentru size-adjust depinde de font. Unele unelte, cum ar fi next/font, o calculează automat.

Un bonus ușor de obținut: back/forward cache (bfcache). Când utilizatorul apasă „Înapoi”, browserul poate restaura instant pagina din memorie, fără reîncărcare și fără deplasări. Paginile care folosesc evenimentul unload pot pierde această optimizare, așa că preferă pagehide sau visibilitychange.

Fă performanța parte din proces

Optimizările făcute o singură dată se erodează în câteva luni: apare un script nou, o imagine necomprimată, o dependență grea. Ca să nu se întâmple asta:

  • Stabilește un buget de performanță: limite pentru dimensiunea JavaScript-ului, numărul de cereri sau scorurile metricilor.
  • Verifică bugetul în CI: Lighthouse CI poate rula audituri la fiecare pull request și poate bloca merge-ul când un prag e depășit.
  • Urmărește datele de teren în timp: raportul din Search Console și propriile date web-vitals îți arată regresiile după deploy-uri.
  • Testează pe dispozitive reale sau cu throttling: în DevTools poți simula un procesor mai lent și o rețea mobilă.

Checklist rapid

  • Imaginea LCP e în HTML, fără loading="lazy", cu fetchpriority="high".
  • Toate imaginile au width și height sau aspect-ratio.
  • Imaginile sunt în format modern și la dimensiunea potrivită.
  • Scripturile terțe sunt încărcate amânat sau la nevoie.
  • Nu există long tasks evidente în handler-ele de click.
  • Fonturile au font-display și un fallback potrivit.
  • Spațiul pentru reclame și embed-uri e rezervat.
  • Datele de teren sunt colectate și urmărite după fiecare lansare.

Pașii următori

  • Rulează PageSpeed Insights pe un site de-al tău și notează care dintre cele trei metrici are cea mai slabă valoare. Începe cu ea.
  • Deschide panoul Performance din Chrome DevTools, înregistrează o interacțiune și caută long tasks.
  • Aprofundează metrica cea mai greu de optimizat în articolul despre INP.
  • Continuă cu roadmap-ul Frontend pentru fundamentele de HTML, CSS și JavaScript pe care se sprijină toate aceste optimizări, iar dacă lucrezi cu React, roadmap-ul React.