---
title: 'Kod Bölme ve Tembel Yükleme'
source: 'https://academia.sh/tr/kurslar/frontend-mimarisi/kod-bolme-ve-tembel-yukleme'
course: 'Uygulama Mimarisi: Yönlendirme, Durum ve Veri'
language: tr
updated: '2026-08-17T18:09:21+00:00'
license: 'CC BY-SA 4.0'
---

# Kod Bölme ve Tembel Yükleme

Paketin rota sınırlarından bölünmesi; bağımlılık kapanışı, kullanım imzasına göre parça oluşturma, ilk yükleme maliyetinin ölçülmesi, gezinme başına ek indirme, önceden getirme ve bölmenin getirdiği bekleme ile hata durumları.

Yönlendirme artık hangi ekranın kime açık olduğunu biliyor. Bilmediği şey, o ekranın
kodunun ne zaman indirileceği. Kuzey Yamaç Ölçüm İstasyonu uygulamasının bütün ekranları
tek bir pakette birlikte iniyor: yalnız oturum açmak için gelen bir kullanıcı, ölçüm
grafiğinin çizim kütüphanesini ve istasyon haritasını da indiriyor.

Rota tanımı bu bölünmeyi yapmak için gereken bilgiyi zaten taşıyor. Hangi bileşenin hangi
yolda kullanıldığı ağacın içinde yazılı; kullanıcının hangi yolda olduğu da çalışma
zamanında bilinen tek şey. **Kod bölme**, bu iki bilgiyi birleştirip paketi rota
sınırlarından ayırma işidir.

## Bağımlılık Kapanışı

Bir rotanın gerçekte neye ihtiyaç duyduğu, doğrudan kullandığı modüllerle sınırlı değildir.
Modüller birbirine bağlıdır: ölçüm grafiği çizim modülünü, o da tarih biçimlendirmeyi
kullanır. Rotanın maliyeti, bağımlılık çizgesinde ondan erişilebilen tüm modüllerin
toplamıdır. Bu kümeye **bağımlılık kapanışı** denir ve Veri Yapıları kursundaki çizge
dolaşımıyla hesaplanır.

Kapanışlar çıkarıldıktan sonra ikinci soru gelir: bir modül birden çok rotada
kullanılıyorsa hangi parçaya girecek? Her rotanın parçasına kopyalanırsa aynı kod birden
çok kez iner. Tek bir ortak parçaya konursa, o parçayı istemeyen rotalar da onu indirmek
zorunda kalır.

Çözüm modülleri **kullanım imzasına** göre gruplamaktır: bir modülün imzası, onu isteyen
rotaların kümesidir. Aynı imzaya sahip modüller birlikte iner, çünkü hep birlikte
gerekirler.

```js
// kod-bolme.mjs — rota bagimlilik kapanisi, kullanim imzasina gore parcalama ve maliyet
const MODUL = {                       // boyut: aktarilan kB, bagimlilik: dogrudan cagrilanlar
  "cekirdek": { boyut: 38, bagimlilik: ["tasarim-sistemi"] },
  "tasarim-sistemi": { boyut: 24, bagimlilik: [] },
  "tablo": { boyut: 18, bagimlilik: ["tasarim-sistemi"] },
  "grafik": { boyut: 96, bagimlilik: ["tasarim-sistemi", "tarih-bicimlendirme"] },
  "harita": { boyut: 120, bagimlilik: ["tasarim-sistemi"] },
  "form-dogrulama": { boyut: 15, bagimlilik: ["tasarim-sistemi"] },
  "tarih-bicimlendirme": { boyut: 12, bagimlilik: [] },
  "disa-aktarma": { boyut: 30, bagimlilik: [] },
};

// Her rota: kendi duzen zinciri boyunca gereken modullerin listesi.
const ROTA = {
  "/oturum": ["form-dogrulama"],
  "/istasyon": ["tablo", "harita"],
  "/istasyon/:kimlik": ["tablo", "tarih-bicimlendirme"],
  "/istasyon/:kimlik/olcum": ["grafik", "tablo", "disa-aktarma"],
  "/istasyon/:kimlik/ayar": ["form-dogrulama"],
};

const GIRIS = ["cekirdek"];           // her ziyarette inen giris noktasi

function kapanis(kokler) {            // bagimlilik cizgesinde erisilen tum modulller
  const gorulen = new Set(), yigit = [...kokler];
  while (yigit.length) {
    const m = yigit.pop();
    if (gorulen.has(m)) continue;
    gorulen.add(m);
    yigit.push(...MODUL[m].bagimlilik);
  }
  return gorulen;
}

const toplam = (kume) => [...kume].reduce((t, m) => t + MODUL[m].boyut, 0);

const girisKapanis = kapanis(GIRIS);
const rotaKapanis = Object.fromEntries(
  Object.entries(ROTA).map(([yol, k]) => [yol, kapanis(k)]));

// Kullanim imzasi: bir modulu isteyen rotalarin kumesi. Ayni imza = ayni parca.
const imza = new Map();
for (const [modul] of Object.entries(MODUL)) {
  if (girisKapanis.has(modul)) continue;                 // giris parcasinda zaten var
  const isteyen = Object.keys(ROTA).filter((y) => rotaKapanis[y].has(modul));
  if (isteyen.length === 0) continue;
  const anahtar = isteyen.join("+");
  if (!imza.has(anahtar)) imza.set(anahtar, []);
  imza.get(anahtar).push(modul);
}

console.log("-- parcalar --");
console.log(`giris${" ".repeat(3)}${String(toplam(girisKapanis)).padStart(4)} kB  ${[...girisKapanis].sort().join(", ")}`);
const parcaAdi = new Map();
let no = 0;
for (const [anahtar, moduller] of [...imza].sort((a, b) => b[0].split("+").length - a[0].split("+").length)) {
  const ad = `parca-${++no}`;
  parcaAdi.set(anahtar, ad);
  console.log(`${ad} ${String(moduller.reduce((t, m) => t + MODUL[m].boyut, 0)).padStart(4)} kB  ${moduller.sort().join(", ")}`);
  console.log(`${" ".repeat(12)}isteyen: ${anahtar.split("+").join("  ")}`);
}

const tekPaket = toplam(new Set([...girisKapanis, ...Object.values(rotaKapanis).flatMap((s) => [...s])]));
const bolunmus = (yol) => toplam(new Set([...girisKapanis, ...rotaKapanis[yol]]));

console.log("-- ilk yukleme maliyeti --");
console.log("rota".padEnd(26), "tek paket", "bolunmus", "kazanc");
for (const yol of Object.keys(ROTA)) {
  const b = bolunmus(yol);
  console.log(yol.padEnd(26), String(tekPaket).padStart(9), String(b).padStart(9),
    `${String(Math.round((1 - b / tekPaket) * 100)).padStart(5)}%`);
}

console.log("-- gezinmede ek indirme --");
for (const [onceki, sonraki] of [
  ["/istasyon", "/istasyon/:kimlik"],
  ["/istasyon/:kimlik", "/istasyon/:kimlik/olcum"],
  ["/istasyon/:kimlik/olcum", "/istasyon/:kimlik/ayar"],
  ["/oturum", "/istasyon"],
]) {
  const inen = new Set([...girisKapanis, ...rotaKapanis[onceki]]);
  const eksik = [...rotaKapanis[sonraki]].filter((m) => !inen.has(m));
  console.log(`${onceki} -> ${sonraki}`.padEnd(52),
    String(eksik.reduce((t, m) => t + MODUL[m].boyut, 0)).padStart(4), "kB ",
    eksik.sort().join(", ") || "-");
}
```

```
-- parcalar --
giris     62 kB  cekirdek, tasarim-sistemi
parca-1   18 kB  tablo
            isteyen: /istasyon  /istasyon/:kimlik  /istasyon/:kimlik/olcum
parca-2   15 kB  form-dogrulama
            isteyen: /oturum  /istasyon/:kimlik/ayar
parca-3   12 kB  tarih-bicimlendirme
            isteyen: /istasyon/:kimlik  /istasyon/:kimlik/olcum
parca-4  126 kB  disa-aktarma, grafik
            isteyen: /istasyon/:kimlik/olcum
parca-5  120 kB  harita
            isteyen: /istasyon
-- ilk yukleme maliyeti --
rota                       tek paket bolunmus kazanc
/oturum                          353        77    78%
/istasyon                        353       200    43%
/istasyon/:kimlik                353        92    74%
/istasyon/:kimlik/olcum          353       218    38%
/istasyon/:kimlik/ayar           353        77    78%
-- gezinmede ek indirme --
/istasyon -> /istasyon/:kimlik                         12 kB  tarih-bicimlendirme
/istasyon/:kimlik -> /istasyon/:kimlik/olcum          126 kB  disa-aktarma, grafik
/istasyon/:kimlik/olcum -> /istasyon/:kimlik/ayar      15 kB  form-dogrulama
/oturum -> /istasyon                                  138 kB  harita, tablo
```

Parça listesi imza gruplamasının sonucudur. Tasarım sistemi her rotada kullanıldığı için
giriş parçasına düşmüştür. Tablo modülü üç rotanın ortağıdır ve tek bir parçada durur;
üç rotaya kopyalanmadığı gibi, onu istemeyen oturum ekranına da yüklenmez. Dışa aktarma ile
grafik modülleri aynı parçada birleşmiştir, çünkü ikisini de yalnızca ölçüm geçmişi ister —
ayrı parçalara konmaları fazladan bir istek doğururdu, hiçbir rotaya kazanç getirmezdi.

## Ölçülen Kazanç

İkinci tablo bölmenin neden yapıldığını sayıyla söyler. Oturum ekranıyla gelen bir
kullanıcı 353 kB yerine 77 kB indirir. Kazanç rotaya göre değişir: haritayı ya da grafiği
isteyen rotalarda daha küçüktür, çünkü asıl ağırlık oradadır.

Buradan iki karar çıkar. Ağır modülü bölmek, hafif modülü bölmekten çok daha fazla kazanç
sağlar; bölme çabası boyuta bakılarak yöneltilir. Ve kazanç, girişin büyüklüğüyle
sınırlıdır: her ziyarette inen 62 kB hiçbir bölmeyle azalmaz. Giriş parçasına gereksiz
modül koymak, bölmeyle kazanılanı geri verir.

Bölmenin bir maliyeti de vardır ve tabloda görünmez: her parça ayrı bir istektir. Modüller,
Araçlar ve Ekosistem kursundaki paketleme dersinde tanımlanan bu maliyet, çok küçük
parçalarda indirilen bayttan büyük olabilir. Ölçüt şudur: bir parça, kaçınılan indirme
kadar kazanç sağlamıyorsa ayrılmaz.

## Gezinme Başına Maliyet

Üçüncü tablo bölmenin ilk yüklemeden sonraki etkisini gösterir. İstasyon ayrıntısından
ölçüm geçmişine geçiş 126 kB'lik yeni bir indirme gerektirir. Kullanıcı bağlantıya
bastığı anda bu indirme başlar; bitene kadar ekranda içerik yoktur.

Tek paketle bu bekleme yoktu — kod zaten inmişti. Bölme, ilk yükleme süresini gezinme
süresine dağıtır. Bu takas her zaman kârlı değildir: kullanıcıların büyük çoğunluğunun
girdiği bir ekranın kodunu geciktirmek, ilk yüklemeden kazanılanı gezinmede fazlasıyla
geri verir.

Takası ortadan kaldıran düzenek **önceden getirmedir**. Bir parça, gerekmeden önce, boşta
kalan zamanda indirilir. Yönlendirmenin bunun için iki iyi işareti vardır: ekranda görünür
hâle gelen bağlantılar ve kullanıcının üzerine geldiği bağlantı. Tarayıcı ve Web Platformu
kursunda tanıtılan kesişim gözlemcisi birincisini, işaretçi olayları ikincisini verir.

Önceden getirme, gerçek isteğin önüne geçmemelidir. Düşük öncelikli yapılır ve boşta
çalışma penceresinde tetiklenir; ölçülü ağ bağlantılarında ise hiç yapılmaz. Aksi hâlde
kullanıcının o an ihtiyaç duyduğu veriyle yarışır.

## Bekleme ve Hata

Bölünmüş bir parçanın yüklenmesi eşzamansız bir işlemdir; dolayısıyla iki yeni durum doğar
ve ikisi de arayüzde karşılanmalıdır.

**Bekleme** durumu, parça inerken ne görüneceğini belirler. Boş bir ekran bırakmak,
kullanıcının gezinmenin çalışmadığını düşünmesine yol açar. Doğru davranış, düzen zincirinin
korunan bölümünü ekranda tutup yalnız değişen yuvaya bir gösterge koymaktır: kabuk yerinde
kaldığı için ekran sarsılmaz. Çok kısa beklemelerde göstergenin görünüp hemen kaybolması da
rahatsız edicidir; gösterge küçük bir gecikmeden sonra çıkarılır.

**Hata** durumu daha kolay unutulur. Ağ kesilmiş olabilir; parça sunucudan silinmiş
olabilir. İkinci durum sanıldığından sıradandır: kullanıcı sayfayı açıkken yeni bir sürüm
yayımlanırsa, eski sayfanın istediği parça adları artık bulunmaz. Bu, Eşzamansız JavaScript
ve Çalışma Zamanı kursundaki Hata ve Yeniden Deneme Kalıpları dersinin doğrudan
uygulamasıdır: yükleme başarısız olduğunda bir kez yeniden denenir, yine başarısız olursa
kullanıcıya sayfayı yenilemesini öneren bir hata görünümü gösterilir.

Üçüncü bir tuzak koruyucularla kesişir. Yetkisiz bir kullanıcının hiç göremeyeceği bir
ekranın parçası indirilmemelidir; koruyucu kararı, parça isteğinden **önce** verilir. Aksi
hâlde bölme, hem gereksiz bayt indirir hem de yetkisiz kullanıcıya o ekranın var olduğunu
bildirir.

## Özet

- Bir rotanın maliyeti, bağımlılık çizgesinde ondan erişilen tüm modüllerin kapanışıdır.
- Modüller kullanım imzasına — kendilerini isteyen rotalar kümesine — göre gruplanır; aynı
  imzalı modüller aynı parçaya girer, böylece ne kopyalanır ne de gereksiz indirilir.
- Bölmenin kazancı girişin büyüklüğüyle sınırlıdır ve ağır modüllerde yoğunlaşır; her parça
  bir istek maliyeti getirdiği için kazanç sağlamayan bölme yapılmaz.
- Bölme, ilk yükleme süresini gezinme süresine dağıtır; boşta zamanda ve düşük öncelikle
  yapılan önceden getirme bu takası hafifletir.
- Parça yüklemesi eşzamansızdır: bekleme durumunda korunan kabuk ekranda tutulur, hata
  durumunda yeniden deneme ve açık bir hata görünümü gerekir.
- Koruyucu kararı parça isteğinden önce verilir; görülemeyecek bir ekranın kodu indirilmez.

## Sonraki Adım

Yönlendirme tamamlandı: adres hangi ekranın görüneceğini, kime açık olduğunu ve hangi kodun
ineceğini belirliyor. Adresin taşıdığı bir durum türü de tanımlandı — süzgeç, sıralama,
sayfa. Uygulamanın geri kalan durumu ise hâlâ dağınık: oturum bilgisi koruyucularda,
istasyon listesi bir yerde, açık paneller bileşenlerin içinde, sunucudan gelen ölçümler bir
başka yerde. Bunların hepsine "durum" demek, aralarındaki en önemli farkı gizler: bazıları
uygulamanın kendi ürettiği, bazıları uzaktaki bir kaynağın kopyası. Sonraki konu durumu
türlerine ayırmakla başlar ve her türün nereye konacağını belirleyen ölçütleri kurar.
