Vite production build yapılandırmasında rollupOptions, manualChunks, terserOptions ve assetsInlineLimit seçeneklerini doğru kullanmak, bundle boyutunu kontrol altında tutmanın temel yoludur.

Vite ile Production Build'i Optimize Etmek İçin Temel Config Ayarları

Vite, geliştirme sürecinde son derece hızlı bir deneyim sunar; ancak üretim ortamına gönderilen dosyaların boyutu ve yapısı, varsayılan ayarlarla her zaman en iyi sonucu vermez. Rollup tabanlı build sürecinin ayrıntılarına inmek, hem ilk yükleme sürelerini hem de sonraki sayfa geçişlerini doğrudan etkiler.

Yapılandırma dosyasındaki birkaç satırlık değişiklik bazen bundle boyutunu belirgin ölçüde küçültür, bazen de tam tersi etki yaratır. Hangi seçeneğin ne zaman işe yarayıp ne zaman gereksiz kaldığını anlamadan yapılan her ayar, tahmin yürütmekten öteye geçemez.

vite.config.ts içindeki build bloğu, hedef tarayıcı, chunk bölme, minifikasyon ve asset inlining kararlarını tek yerde toplar. Her ayar ne yaptığını, hangi durumda uygulanması gerektiğini ve hangi durumda bırakılması gerektiğini ayrı ayrı taşır.

build.target: hangi tarayıcı için derliyorsunuz?

build.target değeri, Vite'ın ürettiği JavaScript'in hangi ortama göre dönüştürüleceğini belirler. Varsayılan modules değeri, <script type="module"> destekleyen tarayıcıları hedefler ve ES2020 seviyesine yakın bir çıktı üretir. Daha geriye gitmek için es2015 veya es2019 kullanılabilir.

esnext hedefi, Vite'ın en az dönüştürme yaptığı seçenektir. Modern bir SPA veya dahili araç geliştiriyorsanız ve kullanıcı kitleniz güncel tarayıcılarda ise esnext doğru tercih olabilir; polyfill ve transpile yükü minimumda kaldığı için bundle boyutu daha küçük kalır. Genel kullanıcı kitlesi olan bir site içinse esnext risklidir.

Hedefi gereğinden geriye çekmenin bir maliyeti vardır. async/await gibi yapılar Rollup tarafından yeniden yazılır, üretilen kod büyür; gereksiz transpile hem daha büyük bundle hem de daha yavaş çalışma zamanı anlamına gelir. Kararı net bir hedef tarayıcı listesine göre verin, sezgiyle değil.

Çok platformlu projeler için target bir dizi de alabilir: ['es2020', 'edge88', 'firefox78', 'chrome87', 'safari14']. Bu şekilde Vite, yalnızca belirtilen tarayıcı kümesine göre gerekli dönüşümleri yapar ve gereksiz polyfill yükünü en aza indirir.

rollupOptions.output ile chunk isimlendirme ve hash stratejisi

build.rollupOptions.output bloğu, üretilen dosyaların adlandırılma biçimini ve bölünme davranışını kontrol eder. entryFileNames, chunkFileNames ve assetFileNames alanları, cache busting için hash kullanımını yönetir.

build: {
  rollupOptions: {
    output: {
      entryFileNames: 'assets/[name]-[hash].js',
      chunkFileNames: 'assets/[name]-[hash].js',
      assetFileNames: 'assets/[name]-[hash][extname]'
    }
  }
}

Hash kullanımı, CDN veya tarayıcı cache'inde eski dosyanın yeni deploy sonrasında sunulmasını engeller. Hash'i kaldırmak, cache yönetimini basitleştirir gibi görünür; ancak değişmeyen dosyaların bile yeniden indirilmesine yol açabilir.

[hash] yerine [contenthash] kullanmak, içerik değişmediği sürece aynı hash'i korur; bağımlı olmayan chunk'ların cache'de kalması bu sayede mümkün olur. Uygulama mantığı değişip vendor kütüphaneleri değişmemişse vendor chunk'ı yeniden indirilmez.

İsimlendirmede [name]-[hash] kalıbını tutmak, production sunucusunda hangi dosyanın hangi modülden üretildiğini anlamayı kolaylaştırır. Gzip veya Brotli sıkıştırması uygulanan sunucularda hash stratejisi, cache başlıklarının doğru çalışması için ayrıca önem taşır.

manualChunks: vendor bundle bölme ve dinamik import kararı

manualChunks, büyük kütüphaneleri ayrı dosyalara taşımak için kullanılır. En yaygın kullanım, React veya Vue gibi stabil kütüphaneleri vendor adlı bir chunk'a almaktır.

build: {
  rollupOptions: {
    output: {
      manualChunks: {
        vendor: ['react', 'react-dom'],
        router: ['react-router-dom']
      }
    }
  }
}

Uygulama kodu değiştiğinde vendor bundle aynı kalır ve cache'de tutulur. Ancak her şeyi tek vendor chunk'ına koymak, o chunk'ın şişmesine ve ilk yüklemede fazla JavaScript indirilmesine neden olur.

Fonksiyon tabanlı manualChunks daha ince ayar sağlar:

manualChunks(id) {
  if (id.includes('node_modules')) {
    const pkg = id.split('node_modules/')[1].split('/')[0];
    return `vendor-${pkg}`;
  }
}

Her paket ayrı bir dosyaya bölündüğünde HTTP/2 üzerinden paralel yükleme avantajı kullanılabilir. Çok fazla küçük chunk oluştursa istek yükü artar; 10-15 kB altındaki chunk'ları birleştirmek genellikle daha iyi bir denge kurar.

Döngüsel bağımlılıklar bu noktada sorun çıkarabilir. Bir modül birden fazla chunk tarafından import edildiğinde Rollup onu en büyük bağımlı chunk'a dahil eder; bu davranış önceden hesaplanmazsa chunk boyutları beklenmedik biçimde şişer. manualChunks yapılandırmasını değiştirdikten sonra bundle'ı görselleştirerek kontrol edin.

terserOptions ile minifikasyon hassasiyeti

Vite, production build'inde varsayılan olarak esbuild ile minifikasyon yapar. Hızlıdır. Ancak Terser kadar agresif sıkıştırma yapamaz; küçük projelerde bu fark ihmal edilebilir, büyük projelerde ise fark edilebilir boyuta ulaşabilir.

Terser kullanmak için önce paketi yükleyin:

npm install --save-dev terser

Ardından yapılandırmada:

build: {
  minify: 'terser',
  terserOptions: {
    compress: {
      drop_console: true,
      drop_debugger: true
    },
    mangle: {
      safari10: true
    }
  }
}

drop_console: true üretim kodundaki tüm console.* çağrılarını kaldırır. pure_funcs ile yalnızca belirli fonksiyon çağrılarını silmek de mümkündür; yan etkisi olmayan çağrılar için daha güvenli bir seçenektir, ancak drop_console zaten tüm console çağrılarını kapsar, ikisini birlikte kullanmak gereksizdir.

mangle.safari10: true seçeneği eski Safari sürümlerindeki bir Terser hatasını önlemek için eklenir. Hedef kitlenizde eski Safari kullanıcısı yoksa bu satırı atlayabilirsiniz.

Build süresi kritikse esbuild ile kalın. Terser build süresini belirgin biçimde uzatır; CI/CD pipeline'larında bu fark toplam build maliyetini etkiler. Kazanılan küçük boyut avantajını bu maliyetle tartmak gerekir.

assetsInlineLimit: küçük dosyayı bundle'a gömmek ne zaman mantıklı?

build.assetsInlineLimit değeri, kaç byte'ın altındaki asset'lerin base64 olarak JavaScript veya CSS'e gömüleceğini belirler. Varsayılan değer 4096 yani 4 kB'dir.

Küçük ikonlar veya veri URI olarak kullanılan görseller için inline kullanım HTTP istek sayısını azaltır. Sorun şu: inline edilen her dosya bundle boyutuna eklenir ve aynı asset birden fazla yerde kullanılıyorsa her bundle'a ayrı ayrı kopyalanır.

build: {
  assetsInlineLimit: 0 // tüm asset'leri ayrı dosya olarak çıkar
}

Sınırı yükseltmek, küçük görüntülerin JavaScript dosyası içinde taşınmasına yol açar. Görsel ağırlıklı sayfalarda bu, LCP varlıklarının JS yüklenmesini beklemesine neden olabilir. SVG ikonlar için inline makul olabilir; PNG veya WebP görseller için sınırı düşük tutmak genellikle daha iyi sonuç verir.

assetsInlineLimit: 0 ile tüm asset'leri ayrı dosya olarak çıkarmak, cache verimliliğini artırır ve bundle analiz araçlarında dağılımı daha net ortaya koyar. Sayfada istek sayısını azaltmak istiyorsanız inline yerine <link rel="preload"> veya HTTP/2 push hint'lerini tercih edin.

CSS code splitting ve cssMinify seçenekleri

build.cssCodeSplit seçeneği, CSS'in JavaScript chunk'larıyla birlikte bölünmesini kontrol eder. Varsayılan true değeriyle her async chunk kendi CSS dosyasını getirir; kullanılmayan CSS başlangıçta yüklenmez.

false yapıldığında tüm CSS tek bir dosyada toplanır. Küçük uygulamalar için tercih edilebilir, özellikle kritik CSS inline edilmişse ve sayfa sayısı azsa. Büyük uygulamalarda cssCodeSplit: false ilk yükleme boyutunu gereksiz yere şişirir.

cssMinify seçeneği Vite 4.4 itibarıyla ayrı yapılandırılabilir hale geldi:

build: {
  cssMinify: 'lightningcss'
}

lightningcss, esbuild'in CSS minifikasyonundan daha iyi prefix handling ve daha agresif sıkıştırma sağlar. Ancak bazı tarayıcı özelliklerine yönelik destek henüz tam olgunlaşmamış olabilir; kullandığınız CSS özelliklerini farklı tarayıcılarda test edin. CSS custom properties ve nesting gibi modern özellikler için çıktıyı doğrulamak önemlidir.

CSS minifikasyonunu false olarak ayarlamak, esas olarak hata ayıklama amacıyla kullanılır. Production'da sıkıştırılmamış CSS bırakmak, hem transfer boyutunu hem de parse süresini artırır; bu seçeneği üretim ortamına taşımayın.

Sourcemap seçimi: production'da ne açılmalı, ne kapatılmalı?

build.sourcemap seçeneği dört değer alır: true (ayrı .map dosyaları üretir), inline (map'i JS'e gömer), hidden (map dosyası üretilir ancak JS'de referans verilmez) ve false (sourcemap üretilmez).

Production'da true veya hidden kullanımı, hata izleme servislerinin gerçek kaynak satırlarını raporlamasını sağlar. inline değeri map'i doğrudan JS'e gömdüğü için bundle boyutunu dramatik biçimde artırır; production'da kullanmayın.

Hassas iş mantığı içeren projelerde true yerine hidden tercih edilebilir. Map dosyaları sunucuda saklanır, tarayıcıdan doğrudan erişilemez; ancak hata izleme servisleri bu dosyalara ulaşıp anlamlı yığın izleri üretebilir. İki seçenek arasındaki fark teknik değil, erişim kontrolüyle ilgilidir.

false seçeneği en küçük bundle boyutunu verir. Hata izleme altyapısı yoksa ya da dahili araçlar için build yapılıyorsa makul bir seçenektir. Kullanıcıya sunulan uygulamalarda sourcemap olmadan production hatalarını izlemek, yığın izlerini tersine mühendislik yapmayı gerektirir ve bu çoğunlukla ciddi bir zaman kaybıdır.

Burada listelenen seçeneklerin her biri bağımsız değil, birbirini etkileyen kararlar zinciridir. manualChunks yapılandırmayı karmaşıklaştırırken assetsInlineLimit yanlış yapılandırılmış chunk'ları daha da büyütebilir. Yapılandırmanın bütününü görmek için rollup-plugin-visualizer ile bundle'ı görselleştirin; hangi modülün nerede oturduğunu görmeden yapılan ayar, körlemesine denemeye yakındır.

Pratikte bu ayarların çoğu varsayılan değerleriyle makul sonuçlar verir. Asıl etkeni bulmak için önce bundle'ı ölçün, sonra tek bir parametreyi değiştirin ve farkı karşılaştırın. Birden fazla değişkeni aynı anda değiştirmek, hangi ayarın ne kadar etki yarattığını gizler ve sorun gidermeyi zorlaştırır.

Üretim build'i yapılandırması bir kez yapılıp unutulacak bir iş değildir. Bağımlılıklar büyüdükçe, dinamik import'lar arttıkça ve yeni özellikler eklendikçe chunk stratejisini yeniden gözden geçirmek gerekir. Bundle analiz çıktısını CI sürecine dahil etmek, beklenmedik büyümeleri erken fark etmenizi sağlar.