Ders 04 / 20
Biçem Sistemi
Biçem, düğümün görünüşünü belirleyen bir özelliktir ve ağaçta başka her özellik gibi karşılaştırılır. Ders içeriği aynı olan yeni bir biçem nesnesinin düğümü korunan bıraktığını, tek alan değişince güncellenen çıktığını ölçer.
Yayın Güncelleme
Bu derste
- Biçemin ağaçta düğümün bir özelliği olduğunu ve görünüşü o özelliğin taşıdığını açıklar
- İçeriği aynı olan yeni bir biçem nesnesinin düğümü korunan bıraktığını ölçer
- Tek bir biçem alanının değişmesinin düğümü güncellenen yaptığını gösterir
- Biçem değişiminin kardeş düğümlere ve alt ağaca dokunmadığını sayarak doğrular
İçindekiler
Önceki dersin sonunda bir düğümün özelliklerinin düz değerler olduğunu söyledik: bir metin, bir görsel kaynağı. Biçem de bir özelliktir, ama değeri düz bir değer değil, bir nesnedir: genişlik, renk, kenar boşluğu gibi alanları bir arada taşır. Soru şudur: karşılaştırma kuralı bir nesneyi nasıl okur? Aynı alanları taşıyan iki ayrı nesne aynı mıdır, farklı mı? Cevap kuralın özelliklere baktığı dördüncü sorusunda gizlidir ve bu ders onu ölçer.
Biçemler çerçevede genellikle bir yardımcıyla tanımlanır ve düğümün özelliği olarak verilir. Aşağıdaki parça ölçülmez; yalnız biçemin ekranda nerede durduğunu gösterir.
// Platform kodu: styles
const styles = StyleSheet.create({
row: { flexDirection: "row", padding: 8 },
box: { width: 40, height: 40, backgroundColor: "teal" },
});
function Strip() {
return (
<View style={styles.row}>
<View style={styles.box} />
<Text>Etiket</Text>
</View>
);
}
Parçayı satır satır okuyalım. StyleSheet.create alan adlarıyla bir nesne alır ve biçemleri tek bir yerde, adlandırılmış olarak toplar; row ve box birer biçem adıdır. flexDirection: "row" esnek kutu düzeninin ana eksenini belirler: çocuklar yan yana dizilir. Esnek kutu düzeni, bir kapsayıcının çocuklarını bir eksen boyunca yerleştiren düzen kuralıdır; çocukların konumunu tek tek yazmak yerine kapsayıcıya yönü söyleriz. padding: 8 kapsayıcının içinde boşluk bırakır. box biçemi kutunun boyunu ve rengini verir. İşlevin içinde iki yer önemlidir: style={styles.row} kapsayıcının biçemini, style={styles.box} kutunun biçemini düğümün style özelliğine bağlar. Text düğümüne biçem verilmemiştir; çerçevenin varsayılanı geçerlidir.
Biçem Bir Özelliktir
Bu parçanın sonucu önemlidir. Biçem ayrı bir dil ya da ayrı bir kanal değildir; düğümün özellikleri arasında yer alan bir nesnedir. Biçem değişince ağaçta olan şey, başka bir özelliğin değişmesinden farklı değildir. Düğümün tipi aynı, anahtarı aynı ise kural özelliklere bakar; biçem dahil hepsi aynıysa korunan, biri farklıysa güncellenen çıkar. Ölçüm için önce çekirdek gerekir.
// fate.mjs — bir guncellemenin gorunum agacindaki her dugume ne yaptigini siniflandirir export const FATES = ["korunan", "guncellenen", "kurulan", "sokulen"]; export function el(type, props = {}, children = [], key = null) { return { type, props, children, key }; } function segment(node) { return node.key === null ? node.type : `${node.type}[${node.key}]`; } function identity(node, index) { return node.key === null ? `#${index}` : `key:${node.key}`; } function sameProps(a, b) { const names = [...new Set([...Object.keys(a), ...Object.keys(b)])].sort(); return names.every((n) => JSON.stringify(a[n]) === JSON.stringify(b[n])); } function whole(node, path, fate, ops) { const here = `${path}/${segment(node)}`; ops.push({ fate, path: here }); node.children.forEach((child) => whole(child, here, fate, ops)); } export function reconcile(prev, next, path = "", ops = []) { if (prev === null) { whole(next, path, "kurulan", ops); return ops; } if (next === null) { whole(prev, path, "sokulen", ops); return ops; } if (prev.type !== next.type || prev.key !== next.key) { whole(prev, path, "sokulen", ops); whole(next, path, "kurulan", ops); return ops; } const here = `${path}/${segment(next)}`; ops.push({ fate: sameProps(prev.props, next.props) ? "korunan" : "guncellenen", path: here }); const before = new Map(prev.children.map((child, i) => [identity(child, i), child])); const matched = new Set(); next.children.forEach((child, i) => { const id = identity(child, i); matched.add(id); reconcile(before.has(id) ? before.get(id) : null, child, here, ops); }); prev.children.forEach((child, i) => { if (!matched.has(identity(child, i))) reconcile(child, null, here, ops); }); return ops; } export function printOps(ops) { ops.forEach((op) => console.log(`${op.fate}: ${op.path}`)); } export function countFates(ops) { for (const name of FATES) { console.log(`${name}: ${ops.filter((op) => op.fate === name).length}`); } console.log(`komut: ${ops.filter((op) => op.fate !== "korunan").length}`); }
Bu dersin yardımcı dosyası, yön, genişlik ve renk durumundan bir şerit ağacı üretir; ikinci işlev aynı biçemi iki kutuya birden verir. Şerit bir Row düğümüdür; yönü özellik olarak taşır. İlk çocuğu Box, ikincisi etiket metnidir. Kutunun biçemi her çağrıda yeni bir nesne olarak oluşturulur, tıpkı bir çizim işlevinin her çalışmasında olduğu gibi.
// strip.mjs — yon, genislik ve renkten serit agaci uretir import { el } from "./fate.mjs"; export function renderStrip(state) { return el("Row", { direction: state.direction ?? "row" }, [ el("Box", { style: { width: state.width, color: state.color } }), el("Text", { value: state.label }), ]); } export function renderPair(state) { const style = { width: state.width, color: state.color }; return el("Row", { direction: "row" }, [ el("Box", { style }), el("Box", { style }), ]); }
Birkaç Alan Birden
Bir güncelleme biçem nesnesinin birden çok alanını değiştirebilir. İlk ölçümde kutunun hem genişliği hem rengi değişir; kardeş metin ve kapsayıcı aynı kalır.
// main.mjs import { reconcile, printOps, countFates } from "./fate.mjs"; import { renderStrip } from "./strip.mjs"; const before = renderStrip({ width: 40, color: "teal", label: "Etiket" }); const after = renderStrip({ width: 80, color: "orange", label: "Etiket" }); const ops = reconcile(before, after); printOps(ops); countFates(ops);
korunan: /Row guncellenen: /Row/Box korunan: /Row/Text korunan: 2 guncellenen: 1 kurulan: 0 sokulen: 0 komut: 1
İki alan değişti ama komut bir. Akıbet düğüm başınadır: bir düğümde kaç alanın değiştiği, kaç komut gittiğini artırmaz. Gerçek bir uygulamada güncelleme komutu hangi alanların değiştiğini taşır ve yerel taraf onları tek tek uygular; o iş bu derste ölçülmez. Burada ölçülen, düğümün güncellenen çıkmasıdır.
Kapsayıcının Biçemi
Yaygın bir yanılgı, biçem değişince altındaki her şeyin de değiştiğini sanmaktır. Kapsayıcının yönü satırdan sütuna dönünce çocuklar ekranda başka yerlere gider; bu yüzden ağaçta da hepsi değişmiş gibi görünür. Ölçelim.
// main.mjs import { reconcile, printOps, countFates } from "./fate.mjs"; import { renderStrip } from "./strip.mjs"; const before = renderStrip({ direction: "row", width: 40, color: "teal", label: "Etiket" }); const after = renderStrip({ direction: "column", width: 40, color: "teal", label: "Etiket" }); const ops = reconcile(before, after); printOps(ops); countFates(ops);
guncellenen: /Row korunan: /Row/Box korunan: /Row/Text korunan: 2 guncellenen: 1 kurulan: 0 sokulen: 0 komut: 1
Yalnız /Row güncellenen çıktı; çocukların kendi özellikleri değişmediği için ikisi de korunan. Ekranda yer değiştirmeleri, ağaçta bir akıbet değildir. Düzeni yeniden hesaplamak yerel tarafın işidir ve kapsayıcının güncellenmesinin sonucudur. Ağaç, kimin özelliğinin değiştiğini söyler; kimin ekranda kaydığını söylemez. Bu, biçemin bu kurstaki ölçüsünün sınırıdır.
Burada M31/K01’den bir ayrım yapmak gerekir. Orada iki değerin eşit olup olmadığı dilin kendi sorusuydu: eşitlik, bir değerin başka bir değerle aynı olup olmadığını soran bir işlemdi ve sorunun cevabı kendi başına bir sonuçtu. Burada eşitlik yalnızca kuralın girdisidir. Kural iki biçem nesnesinin içeriğini eşit bulursa bununla bir şey yapar: düğümü korunan bırakır. Eşitliğin cevabı değil, ondan çıkan akıbet ölçülür.
Ortak Biçem, İki Düğüm
Biçemin bir kez tanımlanıp birçok yerde kullanılması, bu işin asıl nedenidir. Bir kenar boşluğunu ya da bir rengi her düğümde ayrı ayrı yazmak yerine adı olan tek bir biçem kullanırız. Ağaçta ise bu paylaşım görünmez: her düğüm kendi özelliğini taşır. Biçem değişince, o biçemi kullanan her düğüm kendi içinde bir fark bulur.
// main.mjs import { reconcile, printOps, countFates } from "./fate.mjs"; import { renderPair } from "./strip.mjs"; const before = renderPair({ width: 40, color: "teal" }); const after = renderPair({ width: 40, color: "orange" }); const ops = reconcile(before, after); printOps(ops); countFates(ops);
korunan: /Row guncellenen: /Row/Box guncellenen: /Row/Box korunan: 1 guncellenen: 2 kurulan: 0 sokulen: 0 komut: 2
Tek bir biçem değişti, ama iki düğüm güncellenen çıktı ve komut iki oldu. Biçemin ortak olması komutu paylaştırmaz: kural düğümlere bakar, biçem adlarına değil. Bu, biçemleri bir yerde toplamanın bakım kolaylığı sağladığını, ama güncelleme maliyetini azaltmadığını gösterir. İki düğümü bir arada tutmak istiyorsak, onları tek bir kapsayıcıya bağlayıp kapsayıcının biçemini değiştirmek gerekir; önceki bölümlerde yön değişimi tam böyle tek komutla bitmişti.
İki biçem yaklaşımını yan yana düşünmek yararlıdır. Birinde ortak bir özelliği her düğüme yazarız ve değiştirince hepsi güncellenir. Ötekinde özelliği kapsayıcıya koyarız ve çocukları yerleşim kuralına bırakırız. Hangisi doğrudur sorusunun cevabı, görünüşün gerçekten ortak mı yoksa tesadüfen aynı mı olduğuna bağlıdır. Ağaç bu ayrımı yapmaz; sayı yalnız tercihin sonucunu gösterir. Karar yine yazan kişiye aittir.
Bir yanılgıyı daha açalım: “biçemi hiç değiştirmedim, o halde düğüm hiçbir zaman güncellenmez” düşüncesi. Düğümün biçemi aynı kalsa bile metni, görsel kaynağı ya da basılı durumu değişirse düğüm güncellenen çıkar. Kural özelliklerin hepsine birlikte bakar; biçem yalnız bu özelliklerden biridir. Tersi de doğrudur: yalnız biçemi değişen düğümün içeriği aynı kalır ve yine güncellenen olur.
Durumdan gelen biçem de aynı kurala bağlıdır. Bir düğmenin seçili olduğunda başka renkte görünmesini istediğimizde, biçemi bir koşulla seçeriz: durum seçiliyse bir biçem, değilse ötekini veririz. Ağaç yine yalnız sonucu görür. Seçimin hangi koşuldan geldiği, koşulun nerede yazıldığı ya da kaç satır tuttuğu kuralı ilgilendirmez; ilgilendiren, düğümün ürettiği biçem nesnesinin önceki nesneden içerik olarak ayrılıp ayrılmadığıdır. Koşul her seferinde aynı biçemi seçiyorsa düğüm korunan kalır; farklı bir biçem seçiyorsa güncellenen çıkar. Biçemin tanımı ile biçemin seçimi ayrı işlerdir, ve ağaç yalnız seçimin sonucunu taşır.
Sınırlayıcı Ölçüm
Odak düğüm /Row/Box’tır ve ölçüm iki adımdır. Birincisi, hiçbir şey değişmeden ağacı ikinci kez üretir. Her çağrı kutu için yeni bir biçem nesnesi yaratır: bellekte iki ayrı nesne, ama alanları birebir aynı.
// main.mjs import { reconcile, printOps, countFates } from "./fate.mjs"; import { renderStrip } from "./strip.mjs"; const state = { width: 40, color: "teal", label: "Etiket" }; const ops = reconcile(renderStrip(state), renderStrip(state)); printOps(ops); countFates(ops);
korunan: /Row korunan: /Row/Box korunan: /Row/Text korunan: 3 guncellenen: 0 kurulan: 0 sokulen: 0 komut: 0
Üç düğüm de korunan, komut sıfır. Model özellikleri alan alan, içeriklerine bakarak karşılaştırır; iki nesnenin ayrı olması bir fark sayılmaz. /Row/Box yeni bir biçem nesnesi almasına rağmen yerel tarafa hiçbir komut gitmedi. İkinci adımda yalnız kutunun rengi değişir; genişlik, kardeş metin ve kökün durumu aynı kalır.
// main.mjs import { reconcile, printOps, countFates } from "./fate.mjs"; import { renderStrip } from "./strip.mjs"; const before = renderStrip({ width: 40, color: "teal", label: "Etiket" }); const after = renderStrip({ width: 40, color: "orange", label: "Etiket" }); const ops = reconcile(before, after); printOps(ops); countFates(ops);
korunan: /Row guncellenen: /Row/Box korunan: /Row/Text korunan: 2 guncellenen: 1 kurulan: 0 sokulen: 0 komut: 1
Biçem nesnesinin tek alanı değişti ve /Row/Box güncellenen çıktı; bu, dersin ilk gerçek özellik değişimidir. İki adım birlikte okunur: içerik aynıysa sıfır komut, bir alan farklıysa bir komut. Aradaki farkı nesnenin yeniden yaratılması değil, içeriği belirler. Bu yüzden bu dersin akıbeti korunandır: yeni nesne gelmiş olması hiçbir şeyi değiştirmez; güncellenen yalnız bir alan gerçekten farklı olduğunda çıkar.
TY6. Model biçem nesnelerini içerik üzerinden karşılaştırır. Gerçek bir çerçeve özellikleri nesne kimliğiyle karşılaştırabilir; bu durumda her çalışmada yeniden oluşturulan bir biçem nesnesi her seferinde fark sayılabilir. Bu modelde nesne kimliğiyle karşılaştıran bir yer yoktur ve bu ders o davranışı ölçmez. Neyin ölçüldüğü bellidir: içerik aynıyken kural ne yapıyor. Biçemi bir kez tanımlayıp yeniden kullanmanın gerçek çerçevede neden önerildiğinin bir sebebi de budur; ama bu gerekçenin sayısı burada verilmez.
TY7. Gerçek biçemler bir dizi olarak da verilebilir ve çerçeve onları tek bir nesnede birleştirir. Model biçemi tek bir düz nesne sayar. Dizilerin birleştirilmesi ve geçersiz alanların elenmesi ağaçta görünmez.
Biçem değişimi hiçbir düğümü söküp kurmadı. Tip ve anahtar aynı kaldığı sürece görünüş ne kadar değişirse değişsin düğüm en fazla güncellenen çıkar. Sökmenin yolu tipi ya da anahtarı değiştirmektir; tip değişimini önceki ders ölçtü. Model, bu akıbetin kuralını kurar; model çerçevenin kendisi değildir.
Kısaca, biçem sistemi ağacın dışında bir mekanizma değildir. Biçemler tanım yeri bakımından ayrı bir dosyada, adlandırılmış bir tabloda durabilir; ağaca girdiklerinde ise yalnız bir özellik değeri olurlar. Karşılaştırma, biçemin nerede tanımlandığına bakmaz, düğümün taşıdığı değere bakar. Bu yüzden biçemle ilgili her soru aynı üç soruya iner: tip aynı mı, anahtar aynı mı, özelliklerin içeriği aynı mı? Üçüne de evet ise düğüm korunan kalır ve biçemin nerede, nasıl yazıldığı sonucu değiştirmez.
Biçemle ilgili son bir not: bir biçemin tek bir alanını değiştirmek ile biçem nesnesinin tamamını başka bir nesneyle değiştirmek ağaçta aynı sonucu verebilir. Değişen alan sayısı kuralı ilgilendirmez; ilgilendiren, nesnenin önceki nesneyle içerik olarak eşit olup olmadığıdır.
Özet
- Bu dersin akıbeti: korunan. Sınırlayıcı ölçümde içeriği aynı olan yeni biçem nesnesi
/Row/Box’ı korunan bıraktı ve komut sıfır oldu; ardından tek bir alan değişince aynı düğüm, dersin ilk gerçek özellik değişimiyle güncellenen çıktı. - Biçem, düğümün özellikleri arasında duran bir nesnedir; ağaçta başka her özellik gibi karşılaştırılır.
- Model nesneleri içerikle karşılaştırır: içerik aynıysa yeni nesne bir fark sayılmaz. Nesne kimliğiyle karşılaştırma bu modelde yoktur ve ölçülmez.
- Bir ya da birkaç alan değişince düğüm güncellenen çıkar ve tek komut gider; kardeşler ve çocuklar korunan kalır.
- Kapsayıcının yönü değişince yalnız kapsayıcı güncellenir; çocukların ekranda kayması ağaçta bir akıbet değildir.
Sonraki Adım
Şimdiye kadar ağacı biz değiştirdik: durumu elle iki ayrı değerle verdik. Sıradaki ders, Dokunma ve Etkileşim, ağacı değiştiren olayı ele alır: bir dokunuş bir özelliği değiştirir ve bu değişimin hangi düğümlere ulaştığını ölçer.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.