Merhaba arkadaşlar, bugün performans optimizasyonu yaparken başımı çok ağrıtan bir sorundan ve bulduğum çözümden bahsedeceğim. Sitemizin Google PageSpeed Insights skorunu yükseltmeye çalışıyorduk ve "Reduce JavaScript execution time" uyarısı karşımıza dikilmişti. Analiz ettiğimizde, suçlu GA4 script'iydi! Script yüklenirken ve dataLayer.push işlemleri yapılırken ana thread'i (main thread) blokluyor, özellikle mobil cihazlarda First Input Delay (FID) değerini olumsuz etkiliyordu.
Sorunun Kökeni: Bloke Eden Script
Standart GA4 kurulumu genelde şöyle oluyor:
Buradaki ikinci <script> bloğu async değil! Yani, tarayıcı bu satıra geldiğinde, script'i indirme işlemi async olsa da, bu inline script'in çalıştırılması (execution) ana thread'i bloklayabilir. Ayrıca, sayfa yüklenirken yapılan her gtag('event', ...) çağrısı da aynı şekilde hemen çalıştırılmaya çalışılıyor. GA4 script'i henüz tam yüklenmemişse, bu çağrılar bir kuyrukta bekletiliyor (dataLayer array'ine push ediliyor) ama bu işlemlerin kendisi de hafif de olsa bir yük getiriyor.
Çözüm Stratejim: Yükleme ve Yürütmeyi Ayır
Amacım belliydi: 1) GA4 script'ini kesinlikle async ve ana thread'e etki etmeden yükletmek. 2) Sayfa başlangıcındaki kritik olmayan analytics event'larını, script tamamen hazır olana ve/veya kullanıcı etkileşimine kadar geciktirmek. Yani, "lazy load" mantığını analytics'e uyarlamak.
İşte benim uyguladığım, hemen hemen sıfır etkiyle çalışan yöntem:
Uygulanan Kod: requestIdleCallback ile Akıllı Geciktirme
İlk adım, GA4 script'ini dinamik olarak ve async şekilde enjekte etmek. Ama daha da önemlisi, dataLayer.push işlemlerini requestIdleCallback API'si içine alarak, tarayıcının boş zaman dilimlerine (idle periods) ertelemek. Bu API, tarayıcıya "ana thread boşaldığında bu fonksiyonu çalıştır" deme şansı veriyor.
Sonuçlar ve Çıkarımlar
Bu yapıyı kurduktan sonra Lighthouse testinde JavaScript execution time metriklerinde gözle görülür bir iyileşme oldu. Özellikle düşük güçlü mobil cihazlarda, sayfanın ilk etkileşime geçme süresi kısaldı. GA4 verilerimizde ise hiçbir kayıp yaşamadık, çünkü tüm event'lar sadece biraz daha geç gönderildi. Script'in kendisi async yüklendiği için hiçbir kritik kaynağı (CSS, resim, ana JS dosyaları) bloklamıyor.
Tabii ki, satın alma tamamlanması (purchase) gibi kritik ve anlık gönderilmesi gereken event'lar[/COLOR] için bu geciktirme mekanizmasını kullanmamak gerekiyor. Onlar için doğrudan dataLayer.push veya yüklenmiş olan gtag() fonksiyonunu çağırmak en doğrusu.
Siz sitelerinizde analytics yüklenmesini nasıl optimize ediyorsunuz? Hiç requestIdleCallback veya Partytown gibi çözümler denediniz mi? Benim yöntemimde gözden kaçırdığım bir nokta var mı? Yorumlarda fikirlerinizi bekliyorum!
Standart GA4 kurulumu genelde şöyle oluyor:
HTML:
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-XXXXXXXXXX');
</script>
Buradaki ikinci <script> bloğu async değil! Yani, tarayıcı bu satıra geldiğinde, script'i indirme işlemi async olsa da, bu inline script'in çalıştırılması (execution) ana thread'i bloklayabilir. Ayrıca, sayfa yüklenirken yapılan her gtag('event', ...) çağrısı da aynı şekilde hemen çalıştırılmaya çalışılıyor. GA4 script'i henüz tam yüklenmemişse, bu çağrılar bir kuyrukta bekletiliyor (dataLayer array'ine push ediliyor) ama bu işlemlerin kendisi de hafif de olsa bir yük getiriyor.
Amacım belliydi: 1) GA4 script'ini kesinlikle async ve ana thread'e etki etmeden yükletmek. 2) Sayfa başlangıcındaki kritik olmayan analytics event'larını, script tamamen hazır olana ve/veya kullanıcı etkileşimine kadar geciktirmek. Yani, "lazy load" mantığını analytics'e uyarlamak.
İşte benim uyguladığım, hemen hemen sıfır etkiyle çalışan yöntem:
İlk adım, GA4 script'ini dinamik olarak ve async şekilde enjekte etmek. Ama daha da önemlisi, dataLayer.push işlemlerini requestIdleCallback API'si içine alarak, tarayıcının boş zaman dilimlerine (idle periods) ertelemek. Bu API, tarayıcıya "ana thread boşaldığında bu fonksiyonu çalıştır" deme şansı veriyor.
JavaScript:
// 1. dataLayer'i en başta tanımla (diğer scriptlerin erişimi için)
window.dataLayer = window.dataLayer || [];
// 2. GA4 Script'ini Dinamik ve Async Yükleme Fonksiyonu
function loadGA4Script() {
const script = document.createElement('script');
script.async = true;
script.src = 'https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX';
document.head.appendChild(script);
// Script yüklendikten sonra temel config'i yap
script.onload = function() {
window.gtag = function() {
window.dataLayer.push(arguments);
};
window.gtag('js', new Date());
window.gtag('config', 'G-XXXXXXXXXX');
console.log('GA4 scripti sorunsuz yüklendi ve config edildi.');
};
}
// 3. Ana sayfa yükleme event'larını geciktiren wrapper fonksiyon
function deferDataLayerPush() {
// Eğer tarayıcı requestIdleCallback destekliyorsa, onu kullan.
if ('requestIdleCallback' in window) {
requestIdleCallback(function() {
window.dataLayer && window.dataLayer.push(arguments);
}, { timeout: 2000 }); // Max 2 saniye beklesin, sonra çalışsın.
} else {
// Desteklemiyorsa, en azından bir sonraki event loop'a ertele (setTimeout 0)
setTimeout(function() {
window.dataLayer && window.dataLayer.push(arguments);
}, 0);
}
}
// 4. Sayfa Yüklendiğinde Script'i Başlat
// 'DOMContentLoaded' event'ında değil, 'load' event'ında çalıştırarak daha da geç yükleyebilirsiniz.
window.addEventListener('load', function() {
// Burada requestIdleCallback kullanarak script yüklemeyi bile erteleyebiliriz.
if ('requestIdleCallback' in window) {
requestIdleCallback(loadGA4Script, { timeout: 3000 });
} else {
// Fallback: 1 saniye gecikmeli normal yükleme
setTimeout(loadGA4Script, 1000);
}
});
// KULLANIM ÖRNEĞİ:
// Artık sayfa başında hemen fire etmek istemediğimiz event'ları deferDataLayerPush ile sarmalayabiliriz.
// Örneğin, bir buton tıklamasında hemen çalışsın, ama "page_view" gibi event'lar yukarıdaki load event'ı içinde zaten işlenecek.
Bu yapıyı kurduktan sonra Lighthouse testinde JavaScript execution time metriklerinde gözle görülür bir iyileşme oldu. Özellikle düşük güçlü mobil cihazlarda, sayfanın ilk etkileşime geçme süresi kısaldı. GA4 verilerimizde ise hiçbir kayıp yaşamadık, çünkü tüm event'lar sadece biraz daha geç gönderildi. Script'in kendisi async yüklendiği için hiçbir kritik kaynağı (CSS, resim, ana JS dosyaları) bloklamıyor.
Tabii ki, satın alma tamamlanması (purchase) gibi kritik ve anlık gönderilmesi gereken event'lar[/COLOR] için bu geciktirme mekanizmasını kullanmamak gerekiyor. Onlar için doğrudan dataLayer.push veya yüklenmiş olan gtag() fonksiyonunu çağırmak en doğrusu.
Siz sitelerinizde analytics yüklenmesini nasıl optimize ediyorsunuz? Hiç requestIdleCallback veya Partytown gibi çözümler denediniz mi? Benim yöntemimde gözden kaçırdığım bir nokta var mı? Yorumlarda fikirlerinizi bekliyorum!