Core Web Vitals: LCP, INP ve CLS ne ölçer, sitenizde nasıl iyileştirirsiniz?
Core Web Vitals, Google'ın bir sayfanın kullanıcıya nasıl hissettirdiğini üç sayıyla anlatma biçimi: ana içerik ne kadar çabuk göründü, tıkladığında sayfa ne kadar çabuk tepki verdi, sayfa yüklenirken içerik yerinden oynadı mı. Bu üç sayı arama sıralamasında da bir sinyal, ama asıl önemi başka: üçü de doğrudan kullanıcının sabrını ölçüyor.
Bu yazıda her metriğin neyi ölçtüğünü, "iyi" sınırın nerede olduğunu ve hangi düzeltmenin hangi metriği oynattığını anlatacağım. Örnekleri portföyümdeki kurumsal site hız çalışmasından veriyorum: 2018'den kalma tipik bir WordPress kurumsal temasını aynı içerikle yeniden kurup iki sürümü Lighthouse ile ölçtüğüm bir proje.
Üç metrik, üç eşik
| Metrik | Neyi ölçer | İyi | Kötü |
|---|---|---|---|
| LCP (Largest Contentful Paint) | Görünür alandaki en büyük görsel ya da metin bloğunun ekrana çizildiği an | ≤ 2,5 sn | > 4 sn |
| INP (Interaction to Next Paint) | Tıklama, dokunma ve tuş basışlarından sonra ekranın güncellenmesine kadar geçen süre | ≤ 200 ms | > 500 ms |
| CLS (Cumulative Layout Shift) | Beklenmedik yerleşim kaymalarının toplam puanı | ≤ 0,1 | > 0,25 |
Değerlendirme ziyaretlerin 75. yüzdelik dilimine göre yapılıyor, mobil ve masaüstü ayrı ayrı. Yani ziyaretlerinizin dörtte üçü eşiğin altında kalıyorsa o metrikte "iyi" sayılıyorsunuz. Hızlı bir bilgisayarda gördüğünüz sonuç bu yüzden pek bir şey söylemez; asıl belirleyici, orta segment telefonla mobil veriye bağlanan kullanıcı.
INP, Mart 2024'te FID'in yerini aldı. FID yalnızca ilk etkileşimin bekleme süresine bakıyordu; INP ise sayfa açık kaldığı sürece yapılan etkileşimlerin en kötülerinden birini raporluyor ve işleme ile ekrana çizim süresini de hesaba katıyor.
Laboratuvar ölçümü ile saha verisi aynı şey değil
Lighthouse bir laboratuvar aracı: tek bir sayfa yüklemesini simüle edilmiş yavaş bir cihazda ölçer. LCP ve CLS'yi doğrudan verir, ama sayfaya kimse tıklamadığı için INP'yi ölçemez. Onun yerine Total Blocking Time (TBT) gösterir: ana iş parçacığının 50 ms'den uzun süre kilitli kaldığı anların toplamı. TBT yüksekse INP'nin de kötü çıkma ihtimali yüksektir, çünkü kullanıcının tıklaması o kilitli sürenin bitmesini bekler.
Gerçek kullanıcı verisi (saha verisi) için üç kaynak işe yarıyor:
- PageSpeed Insights: siteniz yeterince trafik alıyorsa Chrome kullanıcılarından toplanan CrUX verisini üstte gösterir.
- Search Console'daki Core Web Vitals raporu: sorunlu URL gruplarını listeler.
- Kendi ölçümünüz:
web-vitalskütüphanesiyle metrikleri kendi analitik uç noktanıza gönderebilirsiniz.
import { onLCP, onINP, onCLS } from 'web-vitals';
function gonder(metrik) {
navigator.sendBeacon('/api/vitals', JSON.stringify({
ad: metrik.name,
deger: metrik.value,
sayfa: location.pathname,
}));
}
onLCP(gonder);
onINP(gonder);
onCLS(gonder);
Trafiği az olan yeni bir sitede CrUX verisi çıkmaz; o durumda laboratuvar ölçümü ve kendi topladığınız veri kalıyor.
LCP: en büyük öğe neden geç geliyor?
LCP'yi dört parçaya ayırarak düşünmek işi kolaylaştırıyor: sunucunun ilk baytı gönderme süresi (TTFB), tarayıcının LCP görselini keşfetmesine kadar geçen gecikme, görselin inme süresi ve indikten sonra ekrana çizilmesine kadar geçen süre. Hangisinin uzun olduğunu bilmeden düzeltme yapmak çoğu zaman boşa emek.
Lojistik firması sitesinin eski sürümünde LCP 22,1 saniyeydi. Sebep ilginçti: ana görsel bir JavaScript slider'ının içindeydi, slider display:none ile başlıyordu ve betik çalışana kadar görsel boyanmıyordu. Görsel çoktan inmiş olsa bile ekrana çizilmesi JavaScript'i bekliyordu. Üstüne her biri 300 KB civarında 1600 px JPEG'ler ve <head> içinde bloklayan stil ve betikler eklenince tablo netleşiyor.
Uygulanan düzeltmeler:
<link rel="preload" as="image" type="image/avif" fetchpriority="high"
imagesrcset="hero-480.avif 480w, hero-800.avif 800w, hero-1200.avif 1200w, hero-1600.avif 1600w"
imagesizes="100vw">
<picture>
<source type="image/avif" sizes="100vw"
srcset="hero-480.avif 480w, hero-800.avif 800w, hero-1200.avif 1200w, hero-1600.avif 1600w">
<source type="image/webp" sizes="100vw"
srcset="hero-480.webp 480w, hero-800.webp 800w, hero-1200.webp 1200w, hero-1600.webp 1600w">
<img src="hero-1200.jpg" width="1600" height="1067" fetchpriority="high"
alt="Gün batımında otoyolda ilerleyen lojistik firması tırı">
</picture>
Slider'ı tamamen kaldırdım; ana görsel artık düz HTML'de, tarayıcı onu ilk taramada buluyor ve öncelikli indiriyor. 800 px AVIF sürümü yaklaşık 37 KB. Sonuç: LCP 22,1 saniyeden 1,5 saniyeye indi. Ana görselin önüne loading="lazy" koymamak da bu işin parçası; tembel yükleme ekranın altındaki görseller için.
Görsel dışında LCP'yi uzatan başka bir şey varsa genellikle TTFB'dir: yavaş sunucu, önbelleksiz dinamik sayfa, uzak bir veri merkezi. Onun çaresi sayfa önbelleği ya da statik üretim.
INP: ana iş parçacığını boş tutun
INP kötüyse sebep neredeyse her zaman aynı: kullanıcı tıkladığında tarayıcı başka bir işle meşgul. Uzun bir başlangıç betiği, ağır bir üçüncü parti etiketi ya da tek seferde yüzlerce DOM düğümünü yeniden çizen bir olay dinleyicisi.
Sitenin eski sürümünde açılışta ana iş parçacığını 600 ms kilitleyen bir betik ve bir açılış popup'ı vardı. Lighthouse'ta TBT 2.366 ms çıkıyordu. İkisini de kaldırdım, sayfanın tek betiği 0,5 KB'a indi ve defer ile yükleniyor. TBT 0 ms oldu.
Kaldıramadığınız bir iş varsa onu parçalara bölün ve aralarda tarayıcıya nefes aldırın:
function bekle() {
return new Promise((r) => setTimeout(r, 0));
}
async function listeyiIsle(ogeler) {
for (let i = 0; i < ogeler.length; i++) {
isle(ogeler[i]);
if (i % 50 === 0) await bekle(); // araya girip ekranı güncelleme fırsatı ver
}
}
Tıklamaya verilen görsel tepkiyi (düğmenin basılı hali, yükleniyor göstergesi) önce yapıp ağır işi sonraya bırakmak da INP'yi doğrudan iyileştirir, çünkü ölçülen şey bir sonraki ekrana çizime kadar geçen süre.
CLS: yer ayırın, sonradan içerik eklemeyin
CLS'nin üç klasik sebebi var: boyutu belirtilmemiş görseller, sonradan üste eklenen bantlar ve bildirimler, geç yüklenen fontların metni yeniden dizmesi.
Lojistik firması sitesinin eski sürümünde çerez çubuğu ve kampanya bandı 1,2 saniye sonra sayfanın üstüne ekleniyordu, bütün içerik aşağı kayıyordu. CLS 0,178'di. Düzeltmeler:
- Kampanya bandının yerini HTML'de baştan ayırdım; bant geldiğinde boş alana oturuyor.
- Çerez bildirimi sabit konumlu bir katmana alındı; içeriği itmiyor, üstünde duruyor.
- Galeri görsellerine
width,heightve CSS'teaspect-ratioverildi. - Fontları woff2'ye çevirip
font-display: swapekledim, kalın ağırlığı önceden yükledim.
CLS 0'a indi.
.promo { min-height: 44px; } /* bant için yer baştan ayrılı */
.cookie { position: fixed; bottom: 16px; } /* içeriği itmez, üstünde durur */
.gallery figure { aspect-ratio: 3 / 2; }
Kısa kontrol listesi
Kurumsal site hız çalışmasında bu düzeltmelerin toplamı Lighthouse mobil performans skorunu 22'den 100'e çıkardı (üç çalıştırmanın medyanı). Kendi sitenizde şu sırayla gidebilirsiniz:
- PageSpeed Insights'ta saha verisi var mı bakın; yoksa Lighthouse mobil ile başlayın.
- LCP öğesinin ne olduğunu bulun. Görselse: düz HTML'de mi, öncelikli mi, doğru boyutta mı, lazy değil mi?
- TBT yüksekse Performance panelinde uzun görevleri bulun; gereksiz betiği kaldırın, gerekeni bölün.
- CLS için bütün görsellere boyut verin, sonradan gelen her öğenin yerini baştan ayırın.
- Değişiklikten sonra birkaç kez ölçün ve medyanı alın; tek ölçüm yanıltabilir.
Ölçmeden başladığınız her optimizasyon tahmindir. Önce en büyük sorunu bulun, onu düzeltin, sonra tekrar ölçün.