LCP'mi %70 Nasıl Düşürdüm
Core Web Vitals'ı iyileştirmek için gelişmiş yöntemleri öğrenin

Web workers ve 2 aşamalı görsel yükleme ile LCP metriklerini iyileştirme
Çoğu zaman görünen alandaki büyük bir görsel öğesi Largest Contentful Paint öğesi haline gelir. Görsel boyutlandırma, görsel sıkıştırma, WebP dönüştürme ve LCP öğesini önceden yükleme gibi tüm Lighthouse en iyi uygulamalarını uyguladıktan sonra bile Largest Contentful Paint yine de Core Web Vitals'ı geçemeyebilir.
Bunu düzeltmenin tek yolu, 2 aşamalı yükleme ve sayfanızı web workers ile iş parçacıklarına ayırarak ana iş parçacığındaki kaynakları serbest bırakmak gibi daha gelişmiş taktikler kullanmaktır.
Bu makalede, Largest Contentful Paint'i nasıl daha da iyileştirebileceğinizi göstereceğim.

Biraz arka plan
Ben bir sayfa hızı tutkunu ve web sitem benim vitrinindir. Ana sayfamda sitemin dünyanın en hızlı sitesi olduğunu gururla iddia ediyorum. Bu yüzden sayfamın mümkün olduğunca hızlı yüklenmesi ve sitemden her damla sayfa hızını sıkıp çıkarmam gerekiyor.
Bugün size göstereceğim teknikler, yetenekli ve adanmış bir geliştirici ekibinin desteği olmadan ortalama bir (WordPress) site için uygulanabilir olmayabilir. Bu tekniği kendi sitenizde uygulayamıyor olsanız bile, makaleyi okumanızı ve sayfa hızı hakkında nasıl düşündüğümü ve değerlendirmelerimin neler olduğunu öğrenmenizi tavsiye ederim.
Sorun: görünen alandaki büyük görseller
Görünen alandaki büyük bir görsel genellikle Largest Contentful Paint öğesi haline gelir. Bu LCP görselinin Core Web Vitals'ı geçememesi sıkça yaşanır. Bu tür sonuçları her gün görüyorum.

Bu öğenin ekranda hızlı görünmesini sağlamanın birkaç yolu vardır:
- LCP öğesini önceden yükleyin. LCP görselini önceden yüklemek, bu görselin tarayıcıya mümkün olan en erken zamanda sunulmasını sağlar.
- Duyarlı görseller kullanın. Masaüstü boyutundaki görselleri mobil cihazlara sunmadığınızdan emin olun.
- Görsellerinizi sıkıştırın. Görsel sıkıştırma, görselin boyutunu büyük ölçüde azaltabilir
- Yeni nesil görsel formatları kullanın. WebP gibi yeni nesil görsel formatları, JPEG ve PNG gibi eski formatları neredeyse her durumda geride bırakır.
- Kritik render yolunu minimize edin. LCP'yi geciktirebilecek JavaScript ve stil dosyaları gibi tüm render engelleyici kaynakları ortadan kaldırın.
Ne yazık ki tüm bu optimizasyonlara rağmen, bazı durumlarda LCP metrikleri hâlâ Core Web Vitals denetimini geçemeyebilir. Neden? Görselin boyutu tek başına LCP'yi geciktirmeye yeter.
Çözüm: 2 aşamalı yükleme ve web workers
Sitemdeki diğer tüm sorunları optimize ettikten sonra uyguladığım çözüm, 2 aşamalı görsel yüklemedir.
Fikir basittir: ilk render'da, son yüksek kaliteli görselle aynı boyutlara sahip düşük kaliteli bir görsel gösterin. Bu görsel görüntülendikten hemen sonra, düşük kaliteli görseli yüksek kaliteli görselle değiştiren süreci başlatın.
Çok temel bir uygulama şuna benzeyebilir: Önce bir görsele load olay dinleyicisi ekleyin. Görsel yüklendiğinde aynı olay dinleyicisi kendini ayırır ve görselin src'si son, yüksek kaliteli görselle değiştirilir.
<img
width="100"
height="100"
alt="some alt text"
src="lq.webp"
onload="this.onload=null;this.src='hq.webp'"
> Aşama 1: düşük kaliteli webp 3-5kb 
Aşama 2: yüksek kaliteli webp 20-40kb 
Bu yeterince basit görünebilir (ve öyledir) ancak render sürecinin erken aşamalarında çok sayıda görseli değiştirmek, ana iş parçacığında aşırı aktiviteye neden olacak ve diğer Core Web Vitals metriklerini etkileyecektir.
Bu yüzden işin bir kısmını bir web worker'a aktarmayı tercih ettim. Web worker yeni bir iş parçacığında çalışır ve mevcut sayfaya gerçek bir erişimi yoktur. Web worker ile sayfa arasındaki iletişim bir mesajlaşma sistemi aracılığıyla yapılır. Açık avantajı, sayfanın ana iş parçacığını kullanmıyor olmamızdır, oradaki kaynakları serbest bırakıyoruz. Dezavantajı ise web worker kullanımının biraz zahmetli olabilmesidir.
Sürecin kendisi o kadar zor değil. DomContentLoaded olayı tetiklendikten sonra sayfadaki tüm görselleri topluyorum. Bir görsel yüklenmişse hemen değiştiriyorum. Yüklenmemişse (görsel lazy load yapıyor olabilir) lazy load sonrasında görseli değiştiren bir olay dinleyicisi ekliyorum.
Sonuç: muhteşem

Web worker ile 2 aşamalı LCP yükleme kodu
İşte 2 aşamalı yükleme ve web worker ile LCP'mi hızlandırmak için kullandığım kod. Ana sayfadaki kod, görselleri çekecek bir web worker'ı çağırır. Web worker sonucu blob olarak ana sayfaya iletir. Blob alındığında görsel değiştirilir.
Worker.js
self.addEventListener('message', async event => {
const newimageURL = event.data.src.replace("/lq-","/resize-");
const response = await fetch(newimageURL)
const blob = await response.blob()
// Send the image data to the UI thread!
self.postMessage({
uid: event.data.uid,
blob: blob,
})
}) Script.js
script.js aktif web sayfasında normal bir script olarak çalışır. Script önce worker'ı yükler. Ardından sayfadaki tüm görselleri tarar. Bu, render sürecinin erken aşamalarında gerçekleşir. Bir görsel zaten yüklenmiş olabilir veya olmayabilir. Düşük kaliteli bir görsel zaten yüklenmişse, değiştirme sürecini hemen başlatır. Henüz yüklenmemişse, görsel yüklendiğinde değiştirme sürecini başlatan bir olay dinleyicisi ekler.
Bir görsel yüklendiğinde, o görsel için benzersiz bir kimlik oluşturulur. Bu, görseli sayfada tekrar kolayca bulmamı sağlar (unutmayın, worker'ın DOM'a erişimi yoktur, bu yüzden görsel DOM Node'unu gönderemiyorum).
Görsel URL'si ve benzersiz kimlik daha sonra worker'a gönderilir.
Worker görseli çektikten sonra script'e blob olarak geri gönderilir. Script sonunda eski görsel URL'sini web worker tarafından oluşturulan blob URL'si ile değiştirir.
var myWorker = new Worker('/path-to/worker.js');
// send a message to worker
const sendMessage = (img) => {
// uid makes it easier to find the image
var uid = create_UID();
// set data-uid on image element
img.dataset.uid = uid;
// send message to worker
myWorker.postMessage({ src: img.src, uid: uid });
};
// generate the uid
const create_UID = () => {
var dt = new Date().getTime();
var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = (new Date().getTime() + Math.random() * 16) % 16 | 0;
dt = Math.floor(dt / 16);
return (c == 'x' ? r : (r & 0x3 | 0x8)).toString(16);
});
return uid;
}
// when we get a result from the worker
myWorker.addEventListener('message', event => {
// Grab the message data from the event
const imageData = event.data
// Get the original element for this image
const imageElement = document.querySelectorAll("img[data-uid='" + imageData.uid + "']");
// We can use the `Blob` as an image source! We just need to convert it
// to an object URL first
const objectURL = URL.createObjectURL(imageData.blob)
// Once the image is loaded, we'll want to do some extra cleanup
imageElement.onload = () => {
URL.revokeObjectURL(objectURL)
}
imageElement[0].setAttribute('src', objectURL)
})
// get all images
document.addEventListener("DOMContentLoaded", () => {
document.querySelectorAll('img[loading="lazy"]').forEach(
img => {
// image is already visible?
img.complete ?
// swap immediately
sendMessage(img) :
// swap on load
img.addEventListener(
"load", i => { sendMessage(img) }, { once: true }
)
})
}) LCP görseli önceden yüklenmiş Core Web Vitals Skoru
CrUX data is 28 days late.
Google provides data 28 days late. CoreDash provides data in real-time. Debug faster.
- Real-Time Insights
- Faster Debugging
- Instant Feedback

