JavaScript ve Performans Analizi
JavaScript Yükü Nasıl Ölçülür?
Bir sayfanın JavaScript yükünü sormak, çoğu zaman "kaç kilobayt?" sorusuna indirgeniyor. Oysa transfer boyutu tek değişken değil. Aynı 300 KB'lık bundle, yüksek güçlü bir masaüstü işlemcide fark edilmeden geçerken orta sınıf bir telefonda sayfayı birkaç saniye dondurabilir. Çünkü indirme bittikten sonra sırada parse, compile ve execution adımları var; hepsinin kendine özgü maliyeti var ve hepsinin ölçüm yöntemi farklı.
JavaScript yükü ölçümünü karmaşık yapan başka bir etken de çoklu boyutların aynı anda devrede olması. Bir script küçük ama erken yüklenebilir ve render'ı bloke edebilir; bir başkası büyük ama geç ve asenkron yüklenebilir, görünür etkisi sınırlı kalabilir. Boyut tek başına ne anlattığını yeterince açıklamaz; zamanlaması, önceliği ve çalıştığı bağlamla birlikte değerlendirilmesi gerekir.
Transfer boyutundan parse ve execution süresine, kullanılmayan koddan third-party payına kadar her katman ayrı bir araç ya da yöntem gerektiriyor. Bu katmanların tamamına bakmadan yapılan optimizasyon, genellikle gerçek darboğazı atlayıp semptomları tedavi eder.
Transfer boyutu ile işlem maliyeti arasındaki fark
Ağ üzerinden gelen JavaScript sıkıştırılmış halde taşınır; Gzip veya Brotli sıkıştırması genellikle boyutu orijinalin yüzde 60–75'ine kadar düşürür. Tarayıcı bu paketi aldıktan sonra önce açar, sonra parse eder, sonra derleme sürecine sokar. Ağ gecikmesi azaldıkça bu adımların toplam süresi içindeki payı artar.
Pratik sonuç şu: 100 KB transfer boyutlu bir script, açıldığında 350 KB'a ulaşabilir ve bu büyüklüğün tamamı parse ve compile sürecinden geçmek zorundadır. Ağ panelinde gördüğünüz boyut, main thread'in gerçekte ne kadar işle karşılaştığını söylemez. Bu yüzden yükü yalnızca ağ transferi üzerinden ölçmek, toplam maliyetin büyük bölümünü görünmez kılar.
Coverage paneli ile kullanılmayan kodun tespiti
Chrome DevTools'un Coverage paneli, sayfanın belirli bir kullanım senaryosunda hangi JS satırlarının çalışıp hangilerinin hiç çalışmadığını bayt hassasiyetiyle gösterir. Kaydı başlatıp sayfayla etkileşime girdikten sonra her dosya için çalışan ve çalışmayan kod oranını kırmızı-yeşil şerit olarak görebilirsiniz.
Bir script dosyasının yüzde 80'i ilk yüklemede hiç çalışmıyorsa bu, code splitting için güçlü bir sinyaldir. Tüm bundle'ı ilk yüklemede göndermek yerine, ihtiyaç duyulduğunda o bölümü yüklemek — dynamic import ile — hem parse maliyetini hem de main thread'in bloke olma süresini doğrudan kısaltır. Coverage paneli bu fırsatı dosya bazında, satır hassasiyetiyle gösterir; tahmin değil somut veri sağlar.
Bundle analiz araçlarının rolü
Webpack Bundle Analyzer, Rollup Visualizer ve benzeri araçlar, build çıktısını görsel bir ağaç haritasına dönüştürür. Her modülün toplam bundle içindeki payı, boyutuna orantılı bir alan olarak görünür. Bu haritada hangi kütüphanenin ne kadar yer kapladığı, hangi modüllerin beklenmedik biçimde büyüdüğü ve hangi bağımlılıkların gereksiz yere dahil edildiği hemen fark edilir.
Örneğin bir tarih formatı kütüphanesinin tüm locale dosyalarını bundle'a çekip çekmediğini; bir UI kütüphanesinden yalnızca iki bileşen kullanılmasına rağmen tüm paketin dahil edilip edilmediğini bu araçlarla tespit etmek mümkün. Ağ paneli veya Lighthouse bu tür iç yapı sorunlarını doğrudan göstermez; bundle analizi build aşamasında yapılan bir ön denetim olarak ayrı bir katman işlevi görür.
Parse ve compile süresini okuma
DevTools Performance panelinde bir sayfa yüklemesini kaydettikten sonra "Main" şeridinde parse ve compile aktivitelerini görebilirsiniz. "Parse HTML", "Compile Script" ve "Evaluate Script" olaylarını genişletince hangi dosyanın ne kadar süre harcadığı görülür. Bu süreler cihazdan cihaza ciddi biçimde değişebilir; aynı script masaüstünde 20 ms alırken orta sınıf bir telefonda 150 ms'yi geçebilir.
Lighthouse'un "Reduce JavaScript execution time" denetimi de bu süreler üzerine inşa edilmiştir. Her script için tahmini parse, compile ve execution süreleri listelenir. Ancak bu değerler Lighthouse'un çalıştığı simüle ortamını yansıtır; gerçek kullanıcı cihazlarındaki dağılım çok daha geniş olabilir. Parse maliyetini doğrudan düşürmenin en etkili yolu ise aynı işlevselliği daha az kod ile sunmak ya da gereksiz kodu hiç yüklememektir.
Ağ gecikmesini JS yükünden ayırt etme
Ağ panelinde bir script dosyasının zaman çizelgesine bakıldığında birden fazla segment görünür: DNS çözümlemesi, bağlantı, bekle (waiting/TTFB) ve içerik indirme. İndirme segmenti kısa ama "waiting" uzunsa sorun JS boyutunda değil sunucu ya da TTFB kaynaklıdır. İndirme segmenti uzunsa bant genişliği ya da dosya boyutu etkendir. İkisini karıştırmak yanlış optimizasyon hedefine yönlendirir.
Throttling seçenekleriyle ağ koşullarını yavaşlatmak bu ayrımı netleştirir. "Fast 3G" koşulunda bir scriptin toplam süresi dramatik biçimde uzuyorsa ve bu uzamanın büyük bölümü indirme segmentindeyse, dosya boyutunu düşürmek anlamlı bir kazanım sağlar. Ama "waiting" segmenti uzuyorsa dosyayı küçültmek bu özel gecikmeye dokunmaz.
Execution süresi ve uzun görevler
JavaScript execution süresi, main thread üzerinde gerçekleşir ve bu süre boyunca kullanıcı etkileşimleri beklemek zorunda kalır. 50 ms'yi aşan görevler uzun görev (long task) olarak sınıflandırılır; birden fazla uzun görev peş peşe geldiğinde INP ve TBT metrikleri doğrudan etkilenir. INP, kullanıcının etkileşimine sayfanın ne kadar gecikmeli yanıt verdiğini ölçer; yoğun JS execution bu gecikmenin başlıca kaynağıdır.
Performance panelinde uzun görevler kırmızı köşegen şeritle işaretlenir. Her görevin hangi fonksiyon çağrısından kaynaklandığı Bottom-Up veya Call Tree görünümünde izlenebilir. Yoğun initialization, büyük liste render'ı veya üçüncü taraf script yükleme sırasında patlayan execution süreleri bu yöntemle tespit edilir. Görevi tespit etmeden öncelik vermek güçtür; bu analiz, kesmek için doğru yeri gösterir.
Third-party JS'in toplam yük içindeki payı
Lighthouse'un "Reduce the impact of third-party code" denetimi, harici kaynaklardan yüklenen scriptlerin toplam JS yükü ve main thread blokaj süresi içindeki payını gösterir. Analitik araçları, chat widget'ları, reklam scriptleri, sosyal medya butonları ve A/B test platformları bu kategoriye girer. Çoğu zaman toplam JS yükünün yüzde 40'ını ya da daha fazlasını third-party scriptler oluşturur.
Third-party yükün özel zorluğu şudur: bu scriptlerin içeriği doğrudan kontrol altında değildir. Ancak yükleme zamanlamasını kontrol etmek mümkündür. Kritik içeriği göstermek için gerekli olmayan bir script defer ile geciktirilebilir ya da kullanıcı etkileşimini bekleyerek facade pattern ile tembel yüklenebilir. Yük içindeki payı görmeden bu kararları vermek güçtür; ölçüm, hangi scriptin ne kadar maliyetli olduğunu netleştirir.
Tree shaking etkisini doğrulama
Tree shaking, kullanılmayan export'ları bundle'dan çıkarma işlemidir. Modern bundler'lar bunu otomatik yapar; ama her kütüphane tree shaking ile uyumlu değildir. CommonJS formatında yazılan modüller, yan etkili (side-effectful) olduğu işaretlenen paketler ya da dinamik import kullanan yapılar tree shaking'den yeterince yararlanamaz.
Etkiyi doğrulamanın yolu şudur: bundle analiz aracını tree shaking öncesi ve sonrası için karşılaştırmak ya da bir kütüphaneden yalnızca bir fonksiyon import edildiğinde tüm paketin bundle'a girip girmediğini kontrol etmek. Coverage paneli de bu kontrolde işe yarar; bir dosyanın büyük bölümü hiç çalışmıyorsa tree shaking etkin çalışmıyor ya da gereksiz bir bağımlılık dahil edilmiş olabilir.
Code splitting sonrası yükü izleme
Code splitting uygulandıktan sonra toplam JS boyutu değişmeyebilir; değişen şey, hangi parçanın ne zaman yüklendiğidir. Başarılı bir code splitting uygulaması, ilk yüklemede gelen JS miktarını azaltır ve geri kalanını kullanıcı ihtiyaç duyduğunda getirir. Bunu doğrulamak için ağ panelinde sayfanın farklı durumlarında (açılış, tıklama, rota değişimi) yüklenen script dosyalarını takip etmek gerekir.
İlk yüklemede gelen toplam JS miktarı, code splitting öncesine kıyasla belirgin biçimde düşmemişse bölümleme doğru noktadan yapılmamış demektir. Dynamic import kullanılmış ama webpack veya rollup konfigürasyonu chunk'ları birleştiriyorsa ya da ortak bağımlılıklar her chunk'a ayrı ayrı dahil ediliyorsa bölümleme kağıt üzerinde var, pratikte etkisiz kalır. Ağ panelindeki dosya listesi ve boyutları bu durumu doğrudan ortaya koyar.
Sayfa tipine göre yük beklentisi
Her sayfa için aynı JS yük eşiğini hedeflemek gerçekçi değildir. Bir içerik ağırlıklı blog sayfası ile bir interaktif uygulama sayfasının ihtiyaç duyduğu JavaScript miktarı doğal olarak farklıdır. Ancak her iki durumda da ölçüm aynı soruyu sorar: bu sayfa için gerçekten gerekli olan JS ne kadar, geri kalan ne kadar fazlalık?
E-ticaret ürün sayfaları, sepet işlemleri ve ödeme adımları tipik olarak farklı JS yük profillerine sahiptir. Ürün listeleri genellikle daha hafif tutulabilirken checkout sayfasında ödeme provider'larının scriptleri ağırlığı artırır. Bu farklılıkları fark etmeden yapılan site genelinde bir optimizasyon, gerçek darboğazı olan sayfaları atlayabilir. Sayfa bazında ölçüm, öncelik sırasını doğru belirlemenin tek yoludur.
Yük azaldığında hangi metrikler değişir
JavaScript yükü azaldığında etkisi birden fazla metriğe dağılır. İlk yüklemede gelen JS miktarı düşerse LCP iyileşebilir; çünkü render'ı bloke eden ya da LCP öğesini geciktiren script daha erken tamamlanır. Main thread üzerindeki yük azalırsa uzun görevler kısalır, TBT düşer. Kullanıcı etkileşimine yanıt süresi iyileşirse INP puanı olumlu etkilenir.
Bu zincirleme etki, JS optimizasyonunu diğer performans çalışmalarından farklı kılar: tek bir kaynağı hedefliyorsunuz ama birden fazla metrik üzerinde iz bırakıyor. Ölçüm sonrası optimizasyon yapılıp tekrar ölçüldüğünde hangi metriğin ne kadar değiştiğini karşılaştırmak, değişimin nereden geldiğini ve neyin daha fazla dikkat gerektirdiğini netleştirir. Yük ölçümü bu döngünün başlangıç noktasıdır; bitişi değil.
JavaScript yükünü ölçmek tek bir rakamla bitmez. Transfer boyutu, parse süresi, execution maliyeti, kullanılmayan kod oranı ve third-party payı — bunların her biri ayrı bir soruya yanıt verir ve ayrı bir araçla görülür. Tek bir boyuta bakıp "yük fazla" ya da "yük makul" demek çoğu zaman yanlış bir karara götürür.
Pratikte en verimli yaklaşım, önce hangi sayfanın hangi katmanda sorun yaşadığını belirlemek, ardından o katmana uygun araçla derine inmektir. Coverage panelinde büyük bir kullanılmayan kod kitlesi görünüyorsa code splitting öncelik alır. Bundle analizinde beklenmedik büyük bir bağımlılık çıkıyorsa o paketle başlamak mantıklıdır. Performance panelinde execution süreleri uzun görev üretiyorsa optimizasyon hedefi oraya kayar.
Her katman kendi sorusuna yanıt verir. Soruyu doğru sormak için hangi katmana bakılacağını bilmek gerekir; bunun için de önce ölçmek.