Webpack Bundle Analyzer treemap çıktısını doğru okumak için stat, parsed ve gzip boyutları arasındaki farkı anlamanız, şişirilmiş bağımlılıkları ve yanlış code splitting kararlarını tanımanız gerekir. Treemap, kaygı kaynağı olmaktan çıkıp somut aksiyon listesine dönüşür: hangi bağımlılık şişmiş, hangi chunk yanlış bölünmüş, hangisi tree-shake edilmemiş.

Webpack Bundle Analyzer Çıktısını Nasıl Yorumlarsınız?

Webpack Bundle Analyzer'ı ilk çalıştırdığınızda karşınıza çıkan görüntü, onlarca farklı renkte iç içe dikdörtgenlerden oluşan bir haritadır. Her dikdörtgen bir modülü temsil eder; büyüklüğü ise o modülün bundle içindeki ağırlığını gösterir. Araç doğru okunduğunda hangi kütüphanenin şiştiğini, hangi sayfanın kodunun neden ortak chunk'a sızdığını ve hangi tree-shaking kararının beklendiği gibi çalışmadığını ortaya koyar.

Kurulum basittir: webpack-bundle-analyzer paketini kurar, webpack config'e BundleAnalyzerPlugin eklersiniz ya da Next.js için @next/bundle-analyzer wrapper'ını kullanırsınız. Çıktı olarak bir HTML dosyası üretilir ve tarayıcıda açılır. Zorluk kurulumda değil, bu çıktıyı doğru yorumlamaktadır; büyük dikdörtgen her zaman sorun değildir ve küçük dikdörtgen her zaman masumiyet işareti değildir.

Treemap'i anlamlı şekilde okumak, "en büyüğü kır" refleksinin ötesine geçmeyi gerektirir. Hangi boyut metriğine baktığınız, hangi chunk hangi sayfaya ait ve bir kütüphanenin iki farklı yerde görünmesi ne anlama geliyor gibi soruları cevaplamadan treemap size tahmin üretir, gerçek bilgi vermez.

Üç boyut katmanı: stat, parsed ve gzip

Webpack Bundle Analyzer varsayılan olarak üç farklı boyut görünümü sunar ve bunların arasındaki fark, hangi sorunu araştırdığınızı belirler.

Stat boyutu, webpack'in modülü işlemeden önceki ham büyüklüğünü gösterir; minification ve tree-shaking henüz uygulanmamıştır. Parsed boyutu, webpack'in bundle'a yazdığı, yani minify edilmiş ve tree-shaking uygulanmış gerçek çıktıyı yansıtır. Gzip boyutu ise parsed çıktının sıkıştırıldıktan sonraki tahmini ağırlığıdır.

Kullanıcıya gerçek maliyeti görmek istiyorsanız gzip'e bakın. Tree-shaking'in beklediğiniz kadar kesip kesmediğini anlamak istiyorsanız stat ile parsed'ı karşılaştırın. Stat'ı parsed'a böldüğünüzde tree-shaking verimliliğini görebilirsiniz; fark beklenenden küçükse bir modül bütün olarak bundle'a giriyor demektir.

Gzip boyutları yanıltıcı olabilir. Benzer içerikli metin büyük oranda sıkışır, ancak zaten yoğun sıkıştırılmış ikili formatlar çok az fayda sağlar. Gzip boyutu küçük görünen bir modül, ayrıştırma ve yürütme süresi açısından hâlâ maliyetli olabilir; ağ transferi tek maliyet kalemi değildir. Yalnızca gzip'e odaklanmak bu yüzden eksik bir resim sunar.

Treemap okurken dikkat çekmesi gereken görsel uyarılar

Treemap'i açtığınızda ilk bakışta bütünü görün, hemen en büyük dikdörtgene tıklamayın. Renk dağılımı size chunk yapısını gösterir; her renk bir chunk'ı ifade eder. Sayfalar arası farklılık beklerken neredeyse tüm harita tek bir renkteyse, code splitting ya hiç yapılmamış ya da işe yaramamış demektir.

Tekrar eden modül adları uyarı işaretidir. Aynı kütüphane adını treemap'in farklı bölgelerinde görüyorsanız, bu kütüphane birden fazla chunk'a kopyalanıyor olabilir. Örneğin lodash hem ana bundle'da hem de lazy-loaded bir bileşende ayrı ayrı görünüyorsa, ortak bir chunk'a taşınmamış demektir ve kullanıcıya çift indirme maliyeti çıkarır.

Beklenmedik büyüklük ikinci uyarıdır. Bir UI bileşen kütüphanesi gzip sonrası 200 KB civarı görünürken, projenizde yalnızca birkaç yardımcı fonksiyon içeren başka bir modül benzer büyüklükte yer kaplıyorsa, o modülü yakından incelemeniz gerekir. Küçük bir araç setinin büyük bir kütüphane kadar yer kaplaması, tree-shaking'in çalışmadığının ya da modül formatının ESM olmadığının işareti olabilir.

Source map dahil olup olmadığını da kontrol edin. Production build'ine source map eklediyseniz, treemap'te göreceğiniz boyutlar kullanıcıya gerçekte indirilenden çok daha büyük görünür. Bu durum, raporlanan rakamların gerçek bundle ağırlığını temsil edip etmediğini sorgulatır; production config'inde devtool ayarını gözden geçirmek gerekir.

Şişirilmiş bağımlılıkları tanımlama

Bağımlılık şişmesinin birkaç farklı görünümü vardır. En yaygın olanı, kullandığınızdan çok daha fazlasını içeren bir kütüphanedir. Klasik örnek: yalnızca bir tarih formatlama işlemi için eklenen ancak treemap'te büyük bir alan kaplayan moment.js. Treemap bu kütüphanenin locale dosyalarını, kullanılmayan format yöntemlerini ve tüm zaman dilimi verisini bundle'a dahil ettiğini açıkça gösterir.

Sığ bir alternatifin işe yarayıp yaramayacağını anlamak için önce parsed boyutuna bakın, sonra kütüphanenin hangi alt modüllerinin dahil edildiğini inceleyin. Yalnızca format ve parse fonksiyonlarını kullanıyorsanız ve bunlar toplam kütüphanenin küçük bir parçasıysa, date-fns gibi ESM ve tree-shakeable bir alternatif ya da doğrudan Intl.DateTimeFormat API'si daha uygun olabilir.

Versiyon çakışması ayrı bir şişme türüdür. Aynı kütüphanenin iki farklı versiyonu treemap'te yan yana görünebilir; örneğin react-dom@17 ve react-dom@18. Bu durum, bağımlılık ağacındaki paketlerin farklı peer dependency beklentilerinden kaynaklanır ve npm ls react-dom ya da yarn why react-dom komutuyla kökü bulunabilir. Treemap sizi bu probleme yönlendirir, ancak çözümü package.json'da resolutions veya overrides ile yapmak gerekir.

Polyfill yığını ayrı bir şişme kaynağıdır. Babel ile birlikte gelen core-js modülleri treemap'te genellikle onlarca küçük dikdörtgen olarak görünür. Hedeflediğiniz tarayıcıların modern JavaScript'i desteklediği bir projede bu dikdörtgenlerin toplamı beklenmedik bir yer kaplayabilir. @babel/preset-env'in targets konfigürasyonunu ve useBuiltIns ayarını gözden geçirmek, gereksiz polyfill'lerin neden dahil edildiğini açıklar.

Yanlış code splitting kararlarını fark etmek

Code splitting, treemap'in en değerli bilgileri verdiği alandır. Başarılı bir code splitting yapısında her route veya büyük özellik kendi chunk'ını taşır; ana bundle sadece kritik yolu barındırır ve lazy-loaded modüller ilk yüklemeye dahil olmaz. Treemap bu hedeften sapmayı anında gösterir.

Ana chunk beklenenden büyük. Gzip sonrası ana bundle 200 KB'ın üzerindeyse ve uygulama birden fazla route içeriyorsa, route bazlı splitting'in ya tanımlanmamış ya da yanlış çalışıyor olma ihtimali yüksektir. React Router veya Next.js kullanan projelerde bu genellikle React.lazy ya da dynamic import eksikliğinden kaynaklanır. Treemap'te bu durumu görmek, hangi bileşenlerin lazy boundary arkasına alınması gerektiğini belirlemenizi sağlar.

Aynı kütüphane birden fazla chunk'ta görünüyor. Code splitting doğru yapılandırılmış olsa da, iki farklı lazy chunk aynı bağımlılığı kullanıyorsa webpack bu bağımlılığı her iki chunk'a ayrı ayrı ekler; ortak bir chunk tanımlanmamışsa. Webpack'in SplitChunksPlugin ayarlarındaki minChunks değeri genellikle bu çoğaltmanın nedenidir. Treemap size "bu bağımlılık iki yerde var" sorusunu sordurur, cevabı webpack config'de bulursunuz.

Bir chunk çok küçük olabilir. Ters sorun da gerçektir. Yüzlerce küçük chunk üretmek, HTTP/2 olsa bile parse overhead'i nedeniyle net kazanç yerine kayıp üretebilir. Her birinin gzip boyutu 1-2 KB'ın altındaysa ve toplamda onlarca chunk varsa, SplitChunksPlugin'in minSize değerini artırmak daha sağlıklı bir yapı yaratır.

Bir chunk'ın içinde olmaması gereken bir modülü de tespit edebilirsiniz. Örneğin admin paneli kodu public sayfaların bundle'ında görünüyorsa, dynamic import sınırı yanlış çizilmiş demektir. Bu kaçaklar treemap'te renge göre görülür: admin bileşenlerini ayrı bir chunk olarak bekliyorsunuz ancak ana chunk'ın rengiyle görünüyorlar.

Tree-shaking hatalarını treemap'te görmek

Tree-shaking, kullanılmayan dışa aktarmaları bundle'dan çıkarmak için modülün ESM (ES Module) formatında olmasını gerektirir. CommonJS formatındaki bir modül tree-shaken edilemez ve webpack onu bütün olarak dahil eder. Treemap, tree-shaking'in çalışmadığını dolaylı olarak gösterir: stat ile parsed boyutu arasında beklediğinizden az fark varsa, modül büyük ihtimalle CJS formatındadır.

Barrel dosyaları (index.js re-exports) yaygın bir tree-shaking sorunudur. Bir kütüphaneden yalnızca tek bir fonksiyon import ettiğinizde, o kütüphanenin barrel dosyası tüm modülü çekiyor olabilir; özellikle internal barrel'ların yan etkileri varsa. Treemap'te bu, küçük bir import'tan beklediğinizden çok daha büyük bir alanın o kütüphaneye tahsis edilmesiyle kendini gösterir.

Yan etki sorununu anlamak için paketin package.json'undaki "sideEffects" alanına bakın. Bu alan false olarak tanımlanmamışsa webpack o paketin tüm modüllerini tree-shaking kapsamı dışında tutar. Bazı paketler hâlâ bu alanı tanımlamaz ve sonuç treemap'te şişmiş bir blok olarak görünür. Çözüm, yerel bir uyumluluk katmanı ya da gerçekten tree-shakeable bir alternatif pakettir.

Kimi durumlarda tree-shaking sorunu kütüphaneden değil, projenizin kendi barrel dosyalarından kaynaklanabilir. Bir components/index.ts dosyası tüm bileşenleri re-export ediyorsa ve bu dosyadan import yapıyorsanız, webpack her bileşeni potansiyel olarak dahil eder. Doğrudan components/Button gibi path'lerden import yapmak bu sorunu çözer.

Azaltma aksiyonlarını önceliklendirme

Treemap analizinden bir eylem listesi çıkarmak için boyut, çoğaltma ve değiştirilebilirlik üçlüsünü birlikte değerlendirmeniz gerekir. En büyük dikdörtgen en kolay çözüleni değildir; aksine büyük ancak birden fazla yerde kopyalanan orta boy bir bağımlılık, net kazanç açısından daha öncelikli olabilir.

İlk bakmak gereken yer: gzip boyutunda 20 KB'ın üzerinde olan ve proje içindeki kullanım yüzeyinin küçük olduğu bağımlılıklar. Bunlar için ya daha hafif bir alternatif bulunabilir ya da ilgili özellik lazy load arkasına alınabilir. Tek bir sayfada kullanılan ağır bir grafik kütüphanesi bu kategoriye girer; ana bundle yerine ilgili sayfa chunk'ına taşındığında, bu kütüphaneyi kullanmayan sayfaların ziyaretçileri söz konusu maliyeti hiç ödemez.

İkinci öncelik: aynı bağımlılığın birden fazla chunk'ta kopyalanması. Bu, SplitChunksPlugin konfigürasyonuyla çözülür ve tek bir config değişikliği tüm kopyaları ortak bir chunk'a toplar. Kütüphane değişikliği gerekmez, kod tabanı değişmez; yalnızca webpack yapılandırması güncellenir ve kazanım doğrudan gzip ölçümüne yansır.

Üçüncü öncelik: tree-shaking'in çalışmadığı CJS formatındaki modüller. Bunlar için çözüm kütüphane değiştirmek ya da doğrudan modül path'inden import yapmaktır. Örneğin import _ from 'lodash' yerine import debounce from 'lodash/debounce' kullanmak, tree-shaking yetersiz olsa bile yalnızca o alt modülü bundle'a ekler ve toplam ağırlığı önemli ölçüde azaltır.

Sıra belirlenince çalışmadan önce mevcut gzip boyutlarını not alın. Değişiklik sonrası yeni bir build alır ve treemap'i tekrar açarsınız; beklenen alanın küçüldüğünü doğrulamak, değişikliğin gerçekten işe yaradığını teyit eder. Araç size neyin büyük olduğunu söyler; değişikliğin işe yarayıp yaramadığını tekrar açtığınızda görürsünüz.

Webpack Bundle Analyzer tek başına performans kazanımı sağlamaz; sadece neye bakmanız gerektiğini gösterir. Asıl iş, bu görüntüden çıkan sorulara cevap bulmaktır: neden bu kadar büyük, gerçekten kullanılıyor mu ve değiştirmek ne kadar riskli? Bu soruları sistematik biçimde yanıtlamak, treemap'i bir kaygı kaynağından somut bir teknik borç listesine dönüştürür.

Her deploy sonrası treemap'i manuel açmak yerine, bundle boyutunu CI sürecine bağlamak daha sürdürülebilirdir. Boyut bütçeleri ve otomatik karşılaştırmalar, treemap'in yalnızca sorun çıktığında değil her değişiklikte okunmasını sağlar; büyüme fark edilmeden birikmez.

Treemap'i ne zaman açmayacağınızı da bilmek gerekir. Küçük, tek sayfalık bir projede saatlerce analiz harcamak anlamsızdır; gzip sonrası 50 KB civarında seyreden bir bundle için daha fazla kazanım alanı kalmaz. Araç, gerçek boyut sorunlarının olduğu, birden fazla route taşıyan ve bağımlılık ağacı zamanla büyümüş projelerde en yüksek değeri üretir.