---
title: 'Sanal Ağaç Tabanlı Çerçeveler'
source: 'https://academia.sh/tr/kurslar/bilesen-tabanli-gelistirme/sanal-agac-tabanli-cerceveler'
course: 'Bileşen Tabanlı Arayüz Geliştirme'
language: tr
updated: '2026-08-17T18:09:13+00:00'
license: 'CC BY-SA 4.0'
---

# Sanal Ağaç Tabanlı Çerçeveler

Durumdan yeni bir ağaç üretip öncekiyle karşılaştıran güncelleme modeli; bir hücrelik değişikliğin ürettiği iş miktarının sayılması, anahtarın uzlaşmadaki rolü ve bellemenin ödünleşimi.

Önceki konu boyunca bileşenler kâğıt üzerinde birleştirildi: bir işlev bir ağaç üretti,
ağaçlar iç içe geçti, özellikler birleşti. Üretilen ağacın belgeye nasıl yansıdığına hiç
bakılmadı.

Bu soruya tek bir yanıt yoktur. Çerçeveler, durum değiştiğinde belgenin hangi bölümünün
güncelleneceğini bulmak için birbirinden ayrı yollar izler ve bu yol, çerçevenin öğrenme
eğrisinden başarım profiline kadar her şeyini belirler. Bu konu dört yolu özellikleriyle
ele alır. İlki, güncellemeyi bir **karşılaştırma** problemi olarak gören ailedir.

## Yeniden Çizim Birimi Bileşendir

Modelin adımları şöyledir. Bir bileşenin durumu değişir. Çerçeve o bileşenin işlevini
yeniden çalıştırır; işlev, güncel durumdan yeni bir ağaç üretir. Bu ağaç belge değildir —
belgeyi tarif eden bellek içi bir yapıdır ve **sanal ağaç** adıyla anılır. Çerçeve yeni
ağacı bir önceki ağaçla karşılaştırır, farkları bulur ve yalnızca farkları belgeye yazar.

Modelin kazancı, bileşen yazan kişinin belgeye hiç dokunmamasıdır. Bildirimsel Oluşturma
dersindeki tek yönlü akış burada tam anlamıyla geçerlidir: durumdan görünüme bir işlev
vardır, tersi yoktur.

Bedeli ise şu cümlede saklıdır: **yeniden çizim birimi bileşendir.** Bir bileşenin
durumundaki değişiklik, o bileşenin ürettiği ağacın tamamının yeniden üretilmesine ve
karşılaştırılmasına yol açar; değişen bölüm ne kadar küçük olursa olsun.

## Bir Hücrelik Değişikliğin Maliyeti

İstasyon sayfasındaki ölçüm tablosunda iki yüz satır var. Tek bir sensörün değeri
güncelleniyor. Bunun ürettiği iş sayılabilir.

```js
// sanal-agac.mjs — yeniden cizim, fark alma ve bir hucrelik degisikligin maliyeti
let sayac;
const sifirla = () => (sayac = { bilesen: 0, dugum: 0, oznitelik: 0, yama: 0, atlanan: 0 });

const h = (ad, ozn = {}, ...cocuklar) => ({ ad, ozn, cocuklar: cocuklar.flat().filter((c) => c != null) });
const yazi = (d) => ({ ad: "#metin", ozn: {}, cocuklar: [], metin: String(d) });

// --- Bilesenler: her cizimde yeniden calisir ve yeni bir agac uretir ---
const Hucre = ({ deger }) => { sayac.bilesen++; return h("td", { class: "deger" }, yazi(`${deger.toFixed(1)} °C`)); };
const Satir = ({ ad, deger }) => {
  sayac.bilesen++;
  return h("tr", { anahtar: ad }, h("th", { scope: "row" }, yazi(ad)), Hucre({ deger }));
};
const Tablo = ({ satirlar, ciz }) => {
  sayac.bilesen++;
  return h("table", { class: "olcum" },
    h("caption", {}, yazi("Kuzey Yamaç")),
    h("tbody", {}, ...satirlar.map(ciz)));
};

// --- Belleme: ozellikleri degismeyen bilesen onceki agacini aynen dondurur ---
function belle(bilesen) {
  const onbellek = new Map();
  return (o) => {
    const anahtar = o.ad;
    const kayit = onbellek.get(anahtar);
    if (kayit && Object.keys(o).every((a) => kayit.ozellikler[a] === o[a])) { sayac.atlanan++; return kayit.agac; }
    const agac = bilesen(o);
    onbellek.set(anahtar, { ozellikler: { ...o }, agac });
    return agac;
  };
}

// --- Fark alma ---
function fark(eski, yeni) {
  if (eski === yeni) { sayac.dugum++; return; }        // ayni basvuru: alt agaca hic inilmez
  sayac.dugum++;
  if (eski.ad !== yeni.ad) { sayac.yama++; return; }
  if (eski.metin !== undefined || yeni.metin !== undefined) {
    if (eski.metin !== yeni.metin) sayac.yama++;
    return;
  }
  for (const a of new Set([...Object.keys(eski.ozn), ...Object.keys(yeni.ozn)])) {
    sayac.oznitelik++;
    if (eski.ozn[a] !== yeni.ozn[a]) sayac.yama++;
  }
  const anahtarli = yeni.cocuklar.length > 0 && yeni.cocuklar.every((c) => c.ozn?.anahtar !== undefined);
  if (!anahtarli) {
    const n = Math.max(eski.cocuklar.length, yeni.cocuklar.length);
    for (let i = 0; i < n; i++) {
      if (!eski.cocuklar[i] || !yeni.cocuklar[i]) { sayac.yama++; continue; }
      fark(eski.cocuklar[i], yeni.cocuklar[i]);
    }
    return;
  }
  const eskiHarita = new Map(eski.cocuklar.map((c, i) => [c.ozn.anahtar, { dugum: c, sira: i }]));
  const eskiSira = yeni.cocuklar.map((c) => eskiHarita.get(c.ozn.anahtar)?.sira ?? -1);
  const yerindeKalan = enUzunArtanAltDizi(eskiSira);   // tasinmasi gerekmeyen dugumler
  yeni.cocuklar.forEach((c, i) => {
    const bulunan = eskiHarita.get(c.ozn.anahtar);
    if (!bulunan) { sayac.yama++; return; }            // yeni dugum: eklenir
    if (!yerindeKalan.has(i)) sayac.yama++;            // en az sayida tasima
    fark(bulunan.dugum, c);
    eskiHarita.delete(c.ozn.anahtar);
  });
  sayac.yama += eskiHarita.size;                       // kalanlar silinir
}

// En uzun artan alt dizi: tasinmasi gerekmeyen dugumlerin dizinlerini verir.
function enUzunArtanAltDizi(dizi) {
  const oncul = new Array(dizi.length).fill(-1);
  const kuyruk = [];
  for (let i = 0; i < dizi.length; i++) {
    if (dizi[i] < 0) continue;
    let alt = 0, ust = kuyruk.length;
    while (alt < ust) { const orta = (alt + ust) >> 1; if (dizi[kuyruk[orta]] < dizi[i]) alt = orta + 1; else ust = orta; }
    if (alt > 0) oncul[i] = kuyruk[alt - 1];
    kuyruk[alt] = i;
  }
  const sonuc = new Set();
  for (let k = kuyruk[kuyruk.length - 1]; k !== undefined && k !== -1; k = oncul[k]) sonuc.add(k);
  return sonuc;
}

const raporla = (baslik) =>
  console.log(`${baslik.padEnd(36)} ${String(sayac.bilesen).padStart(9)} ${String(sayac.atlanan).padStart(8)} ` +
    `${String(sayac.dugum).padStart(9)} ${String(sayac.oznitelik).padStart(11)} ${String(sayac.yama).padStart(5)}`);

const SATIR_SAYISI = 200;
const veri = (kaydirma = 0) =>
  Array.from({ length: SATIR_SAYISI }, (_, i) => ({ ad: `sensör-${String(i).padStart(3, "0")}`, deger: -10 + i * 0.1 + kaydirma }));

console.log("senaryo                              bileşen  atlanan     düğüm    öznitelik  yama");

// 1. Belleme yok: tek hucre degisiyor.
sifirla();
const v1 = veri();
const eski1 = Tablo({ satirlar: v1, ciz: Satir });
const v2 = v1.map((s, i) => (i === 120 ? { ...s, deger: 42.5 } : s));
const yeni1 = Tablo({ satirlar: v2, ciz: Satir });
fark(eski1, yeni1);
raporla("bellemesiz, tek hücre değişti");

// 2. Belleme var: ozellikleri degismeyen satirlar atlanir.
sifirla();
const bellenmisSatir = belle(Satir);
const eski2 = Tablo({ satirlar: v1, ciz: bellenmisSatir });
const yeni2 = Tablo({ satirlar: v2, ciz: bellenmisSatir });
fark(eski2, yeni2);
raporla("bellemeli, tek hücre değişti");

// 3. Hicbir sey degismedi (ust bilesen baska bir nedenle cizildi).
sifirla();
const eski3 = Tablo({ satirlar: v1, ciz: Satir });
const yeni3 = Tablo({ satirlar: v1, ciz: Satir });
fark(eski3, yeni3);
raporla("bellemesiz, hiçbir şey değişmedi");

// 4. Basa bir satir eklendi — anahtarli uzlasma.
sifirla();
const yeniSatir = { ad: "sensör-yeni", deger: 3.3 };
const eski4 = Tablo({ satirlar: v1, ciz: Satir });
const yeni4 = Tablo({ satirlar: [yeniSatir, ...v1], ciz: Satir });
fark(eski4, yeni4);
raporla("başa satır eklendi (anahtarlı)");

// 5. Ayni ekleme, anahtar yokken: her satir kaydigi icin karsilastirma eslesmiyor.
sifirla();
const SatirAnahtarsiz = ({ ad, deger }) => {
  sayac.bilesen++;
  return h("tr", {}, h("th", { scope: "row" }, yazi(ad)), Hucre({ deger }));
};
const eski5 = Tablo({ satirlar: v1, ciz: SatirAnahtarsiz });
const yeni5 = Tablo({ satirlar: [yeniSatir, ...v1], ciz: SatirAnahtarsiz });
fark(eski5, yeni5);
raporla("başa satır eklendi (anahtarsız)");
```

```
senaryo                              bileşen  atlanan     düğüm    öznitelik  yama
bellemesiz, tek hücre değişti              802        0      1004         601     1
bellemeli, tek hücre değişti               404      199       208           4     1
bellemesiz, hiçbir şey değişmedi           802        0      1004         601     0
başa satır eklendi (anahtarlı)             804        0      1004         601     1
başa satır eklendi (anahtarsız)            804        0      1004         401   401
```

## Sayıların Okunması

İlk satır modelin özetidir. Belgeye yazılan tek bir yama var; ona ulaşmak için 802 bileşen
çağrısı, 1004 düğüm karşılaştırması ve 601 öznitelik karşılaştırması yapıldı. Oran yaklaşık
bin altı yüze bir.

Bu bir kusur değil, modelin tanımıdır. Çerçeve neyin değiştiğini **bilmez**; öğrenmek için
karşılaştırır. Karşılaştırmanın maliyeti değişikliğin büyüklüğüyle değil, üretilen ağacın
büyüklüğüyle orantılıdır.

Üçüncü satır bunu en açık biçimde gösteriyor: hiçbir şey değişmediğinde de aynı 802 çağrı
ve 1004 karşılaştırma yapılıyor, yalnızca sonuçta sıfır yama çıkıyor. Değişiklik olmaması
işi azaltmıyor.

Sayılar iki bileşen daha içeriyor. Bileşen çağrısı sayısı, üretilen ağacın yeniden
kurulmasıdır; her çağrı yeni nesneler ayırır ve öbekte iş üretir. Düğüm karşılaştırması ise
ağacı gezme maliyetidir. İkisi ayrı ayrı ölçülür, çünkü ayrı ayrı azaltılırlar.

## Anahtar Uzlaşmanın Girdisidir

Dördüncü ve beşinci satırlar arasındaki fark, bu ailedeki en somut karar noktasını gösteriyor.

Başa bir satır eklendiğinde, anahtar varsa uzlaşma her satırı kimliğiyle eşleştirir. Eski
sıradaki 200 satır yeni sırada da aynı göreli düzendedir; taşınması gereken düğüm yoktur ve
tek yama yeni satırın eklenmesidir. (Örnekteki taşıma sayımı en uzun artan alt dizi
hesabına dayanır: yerinde kalabilecek en büyük düğüm kümesi bulunur, yalnızca dışarıda
kalanlar taşınır.)

Anahtar yoksa karşılaştırma **konuma** göre yapılır. Yeni listenin birinci öğesi eski
listenin birinci öğesiyle, ikincisi ikincisiyle eşleştirilir. Başa ekleme her satırı bir
konum kaydırdığı için hiçbir eşleşme tutmaz: 200 satırın adı ve değeri farklı çıkar ve 401
yama üretilir. Belgede aslında tek bir satır eklenmiş olsa bile, 401 yazma işlemi yapılır.

Sonucun ötesinde bir yan etkisi daha vardır. Konuma göre eşleşen düğümler yeniden
kullanıldığı için, o düğümlere bağlı belge durumu — odak, kaydırma konumu, girdi alanının
yazılmış içeriği — yanlış satırda kalır. Bu, listelerde anahtar kullanmanın bir başarım
tercihi değil, doğruluk gereği olmasının nedenidir.

## Bellemenin Ödünleşimi

İkinci satır, işin nasıl azaltıldığını gösteriyor. Satır bileşeni bellendiğinde, özellikleri
değişmeyen 199 satır yeniden çalışmıyor ve önceki ağacını aynen döndürüyor. Fark alma bu
düğümlere ulaştığında başvuruların aynı olduğunu görüp alt ağaca hiç inmiyor: düğüm
karşılaştırması 1004'ten 208'e, öznitelik karşılaştırması 601'den 4'e düşüyor.

Kazanç büyük, ama koşullu. Belleme yalnızca özelliklerin **kimliği** korunduğunda çalışır.
Üst bileşen her çizimde yeni bir nesne ya da yeni bir işlev üretip özellik olarak
veriyorsa, sığ karşılaştırma her seferinde başarısız olur ve belleme hiçbir şey kazandırmaz —
üstelik karşılaştırmanın kendi maliyetini ekler. Türetilmiş Değerler dersindeki kimlik
kararlılığı sorunu, bu ailede bir başarım aracına dönüşür.

İkinci bedel görünürlüktür. Belleme elle yapılan bir işlemdir: hangi bileşenin belleneceğine
ve hangi değerlerin kimliğinin korunacağına insan karar verir. Karar yanlış verildiğinde
sonuç bir hata değil, sessiz bir yavaşlamadır.

## Ailenin Profili

Bu ailenin özelliklerini birkaç maddede toplamak, sonraki derslerdeki karşılaştırmanın
temelini verir.

Güncelleme birimi bileşendir; ince tanelilik bileşen sınırına kadar iner, daha aşağı inmez.
Çalışma zamanı, fark alma algoritmasını ve ağaç temsilini taşımak zorundadır; bu, indirilen
kodun sabit bir bölümüdür. Bileşen işlevi saf bir dönüşüm olarak yazıldığı için üretim
işi bölünebilir: çerçeve karşılaştırmayı parçalara ayırıp aralara başka iş sokabilir, çünkü
sanal ağaç belgeye yazılmadan önce hiçbir görünür etki yaratmaz. Buna karşılık aynı saflık,
değişikliğin nereden geldiğini kaydeden bir defter tutulmadığı anlamına gelir; bilgi her
güncellemede karşılaştırmayla yeniden üretilir.

## Özet

- Bu ailede güncelleme, durumdan yeni bir ağaç üretip önceki ağaçla karşılaştırmaktır;
  yeniden çizim birimi bileşendir.
- Karşılaştırmanın maliyeti değişikliğin büyüklüğüyle değil üretilen ağacın büyüklüğüyle
  orantılıdır: iki yüz satırlık tabloda tek hücrelik değişiklik 802 bileşen çağrısı ve 1004
  düğüm karşılaştırması üretti, belgeye tek yama yazıldı.
- Hiçbir şey değişmediğinde de aynı iş yapılır; yalnızca yama sayısı sıfır çıkar.
- Anahtar, uzlaşmanın kimlik girdisidir: anahtarlı listede başa ekleme tek yama, anahtarsız
  listede 401 yama üretti ve konuma göre eşleşen düğümlerde belge durumu yanlış satırda kalır.
- Belleme, özellikleri değişmeyen alt ağaçlara hiç inilmemesini sağlar; örnekte düğüm
  karşılaştırması 1004'ten 208'e indi.
- Belleme özelliklerin kimlik kararlılığına bağlıdır ve elle yapılır; yanlış uygulandığında
  sessizce etkisiz kalır.

## Sonraki Adım

Bu ailedeki iş miktarının kaynağı tek bir eksikti: çerçeve neyin değiştiğini bilmiyordu,
bulmak için karşılaştırıyordu. Bilseydi karşılaştırmaya hiç gerek kalmazdı. Bilmenin yolu,
bir değerin nerede okunduğunu okuma anında kaydetmektir — o zaman değer yazıldığında,
güncellenmesi gereken yerlerin listesi zaten elde olur. Sonraki ders, bu defteri çalışma
zamanında tutan aileyi ele alır ve aynı iki yüz satırlık tabloda aynı hücre değiştiğinde
yapılan iş miktarını bu dersteki sayılarla karşılaştırır.
