JavaScript Bundle Optimizasyonu: Tree Shaking ve Code Splitting

JavaScript Bundle Optimizasyonu: Tree Shaking ve Code Splitting
JavaScript, modern web uygulamalarının en büyük performans darboğazlarından biridir. Tarayıcı JavaScript'i indirmek, parse etmek, derlemek ve çalıştırmak zorundadır — her adım zaman alır. HTTP Archive'a göre medyan bir web sayfası 500KB'ın üzerinde JavaScript yükler ve bu süre mobil cihazlarda ciddi gecikmelere yol açar. Bu rehberde tree shaking, code splitting, lazy loading ve bundle analiz tekniklerini kullanarak JavaScript yükünü nasıl minimize edeceğinizi öğreneceksiniz.
JavaScript'in Performans Maliyeti
JavaScript, aynı boyuttaki bir görselden çok daha maliyetlidir. 200KB'lık bir JPEG indirildikten sonra hızlıca decode edilirken, 200KB'lık JavaScript indirme, parse, derleme ve çalıştırma aşamalarından geçer. Bu durum özellikle düşük güçlü mobil cihazlarda belirginleşir.
- Parse ve compile: V8 motoru JavaScript'i bytecode'a çevirir, bu işlem CPU-yoğundur
- Execution: Kod çalıştırılırken ana thread bloke olur
- Memory: Her kütüphane RAM tüketir, özellikle SPA'larda birikir
Tree Shaking: Kullanılmayan Kodu Eleme
Tree shaking, ES Module'lerin statik yapısını kullanarak kullanılmayan export'ları bundle'dan çıkarır. Webpack, Rollup ve esbuild bu özelliği destekler.
// utils.js - Bu modülden sadece formatDate kullanılıyor
export function formatDate(date) {
return new Intl.DateTimeFormat('tr-TR').format(date);
}
export function formatCurrency(amount) {
return new Intl.NumberFormat('tr-TR', {
style: 'currency', currency: 'TRY'
}).format(amount);
}
export function calculateTax(amount) {
return amount * 0.18;
}
// app.js - Sadece formatDate import ediliyor
// Tree shaking ile formatCurrency ve calculateTax bundle'a dahil edilmez
import { formatDate } from './utils';
console.log(formatDate(new Date()));
Tree shaking'in etkili çalışması için dikkat edilmesi gerekenler:
// webpack.config.js - Tree shaking konfigürasyonu
module.exports = {
mode: 'production', // Production modda tree shaking otomatik aktif
optimization: {
usedExports: true, // Kullanılmayan export'ları işaretle
minimize: true, // Terser ile dead code elimination
sideEffects: true, // package.json sideEffects alanını kontrol et
concatenateModules: true // Module concatenation (scope hoisting)
}
};
// package.json - Side effects tanımlama
{
"name": "my-library",
"sideEffects": false, // Tüm modüller pure (side effect yok)
// veya belirli dosyaları hariç tut:
"sideEffects": [
"*.css",
"./src/polyfills.js"
]
}
require()) modülleri statik olarak analiz edilemez, bu nedenle tree shaking çalışmaz. Kütüphanelerin ES Module (ESM) versiyonlarını tercih edin. package.json'daki module veya exports alanı ESM giriş noktasını belirtir.
Code Splitting: Kodu Parçalara Ayırma
Code splitting, uygulamanızı mantıksal parçalara bölerek sadece gerekli kodu yüklemenizi sağlar. Üç temel strateji vardır:
// 1. Dynamic Import ile route-based code splitting
// React.lazy ile component-level splitting
import React, { lazy, Suspense } from 'react';
const Dashboard = lazy(() => import('./pages/Dashboard'));
const Settings = lazy(() => import('./pages/Settings'));
const Analytics = lazy(() =>
import(/* webpackChunkName: "analytics" */ './pages/Analytics')
);
function App() {
return (
<Suspense fallback={<LoadingSpinner />}>
<Routes>
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/settings" element={<Settings />} />
<Route path="/analytics" element={<Analytics />} />
</Routes>
</Suspense>
);
}
// 2. Event-based lazy loading
async function openHeavyModal() {
const { HeavyModal } = await import('./components/HeavyModal');
renderModal(HeavyModal);
}
document.getElementById('openModal').addEventListener('click', openHeavyModal);
// 3. Vendor splitting - Webpack konfigürasyonu
// webpack.config.js
module.exports = {
optimization: {
splitChunks: {
chunks: 'all',
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all',
priority: 10
},
common: {
minChunks: 2,
priority: 5,
reuseExistingChunk: true
}
}
}
}
};
Bundle Analizi
Optimizasyon yapmadan önce mevcut durumu analiz etmelisiniz:
# webpack-bundle-analyzer kurulumu ve kullanımı
npm install --save-dev webpack-bundle-analyzer
# webpack.config.js'e ekle
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
plugins: [
new BundleAnalyzerPlugin({
analyzerMode: 'static',
reportFilename: 'bundle-report.html',
openAnalyzer: false
})
]
};
# Next.js için @next/bundle-analyzer
npm install @next/bundle-analyzer
# next.config.js
const withBundleAnalyzer = require('@next/bundle-analyzer')({
enabled: process.env.ANALYZE === 'true',
});
module.exports = withBundleAnalyzer({
// next config
});
# Analiz raporu oluştur
ANALYZE=true npm run build
moment.js (300KB+), lodash (70KB+) gibi kütüphaneler bundle'ın büyük kısmını oluşturur. date-fns ve lodash-es gibi tree-shakeable alternatiflere geçiş yaparak dramatik boyut azaltımı sağlayabilirsiniz.
Pratik Optimizasyon Örnekleri
Sık kullanılan kütüphanelerin optimize import yöntemleri:
// Kötü: Tüm lodash'ı import eder (~70KB gzipped)
import _ from 'lodash';
_.debounce(fn, 300);
// İyi: Sadece debounce fonksiyonunu import et (~1KB)
import debounce from 'lodash/debounce';
debounce(fn, 300);
// Kötü: Tüm moment.js (~300KB)
import moment from 'moment';
// İyi: date-fns ile sadece gerekli fonksiyon (~2KB per function)
import { format, parseISO } from 'date-fns';
import { tr } from 'date-fns/locale';
format(parseISO('2025-01-01'), 'dd MMMM yyyy', { locale: tr });
// Kötü: Tüm Material UI ikonları (~5MB!)
import { Delete, Edit, Add } from '@mui/icons-material';
// İyi: Named import ile tree shaking
import Delete from '@mui/icons-material/Delete';
import Edit from '@mui/icons-material/Edit';
import Add from '@mui/icons-material/Add';
Sonuç
JavaScript bundle optimizasyonu, web performansını iyileştirmenin en etkili yollarından biridir. Tree shaking ile kullanılmayan kodu eleyin, code splitting ile sadece gerekli kodu yükleyin ve bundle analizi ile büyük bağımlılıkları tespit edin. Bu tekniklerin kombinasyonu, JavaScript yükünü %50-70 oranında azaltabilir ve kullanıcılarınıza çok daha hızlı bir deneyim sunabilir.