Ders 11 / 19
Varlık İşleme
Görsel, yazı tipi ve statik dosyaların derleme çıktısına girmesi; bağların çizgeye eklenmesi, gömme eşiği kararı, referansların yeniden yazılması, varlık bildirimi ve doğrudan kopyalanan dizin.
İçindekiler
Üç ders boyunca çizge yalnız betik dosyalarından kuruldu. Kuzey Yamaç Ölçüm İstasyonu arayüzünün çıktısı bundan ibaret değildir: ölçüm listesinin biçem dosyası, listedeki her satırın önündeki simge, istasyon yükselti eğrisinin çizimi, arayüzün yazı tipi ve arama motorlarının okuduğu birkaç sabit adlı dosya da yayımlanır.
Bu dosyalar iki ayrı yoldan çıktıya girer. Bir kısmı koddan ya da biçem dosyasından bağlanır; bağ, çizgede bir kenardır ve varlık bir düğüm hâline gelir. Bir kısmı hiçbir yerden bağlanmaz ama yine de yayımlanmak zorundadır. Bu ders iki yolu ayırır ve bağlanan varlıkların geçtiği boru hattını çalıştırır.
Varlık Bir Çizge Düğümüdür
Bir varlığın çizgeye girebilmesi için bağın statik olarak okunabilir olması gerekir. İki standart yazım bunu sağlar.
Biçem dosyasındaki url() bildirimi, göreli bir yolu adres olarak verir. Kaynaktaki yol,
biçem dosyasının bulunduğu dizine göre çözülür.
Koddaki new URL("./x.svg", import.meta.url) yazımı aynı işi betik tarafında yapar. İkinci
argüman modülün kendi adresidir; ifade, modülün konumuna göre çözülmüş bir adres üretir. Bu
yazım standardın parçasıdır ve herhangi bir araca bağlı değildir; bir araç yokken de
çalışır, bir araç varken de derleme zamanında yakalanabilir.
Yakalanamayan yazım, adresin çalışma zamanında kurulmasıdır. "/varlik/" + ad + ".svg"
biçiminde bir birleştirme, derleme sırasında hangi dosyayı gösterdiğini söylemez. Böyle bir
bağ boru hattına girmez: dosya kopyalanmaz, adı yeniden yazılmaz ve çıktıda bulunmayan bir
adrese istek gider. Kural nettir — çalışma zamanında hesaplanan varlık adresleri, doğrudan
kopyalanan dizinden karşılanır.
İki Akış
İşlenen akış, bağlanan varlıkları kapsar. Bu varlıklar boyutlarına göre ya çıktıya ayrı dosya olarak yazılır ya da bağlandıkları dosyanın gövdesine gömülür; her iki durumda da kaynaktaki bağ, çıktıdaki adrese çevrilir.
Doğrudan kopyalanan akış, adı ve konumu değişmeden yayımlanması gereken dosyalar içindir: gezgin kurallarını taşıyan dosya, alan adı doğrulama dosyaları, sabit bir adresten indirilen veri dosyaları. Bu dosyalar taranmaz, adları korunur ve içerikleri değişmez. Ayrımın ölçütü tek bir sorudur: adresi derlemenin belirlemesi serbest mi, yoksa dışarıdan sabitlenmiş mi?
Varlık Ağacı
Aşağıdaki blok varlıkları belirlenimci biçimde üretir. Yazı tipi gövdesi yerine aynı boyutta bir dolgu dosyası konur; boru hattının kararları dosyanın içeriğine değil boyutuna bağlı olduğu için bu, sonucu değiştirmez.
Aşağıdaki blok, önceki derslerde kurulan kaynak/ ağacına varlık bağlarını ekler; o derslerin kurulum blokları bu dizinde önce çalıştırılmalıdır.
#!/usr/bin/env bash mkdir -p varlik genel bicem cat > varlik/simge.svg <<'EOF' <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="none" stroke="#243"> <path d="M2 13 L6 5 L9 10 L11 7 L14 13 Z"/><circle cx="12" cy="3" r="2"/> </svg> EOF node --input-type=module -e ' import { writeFileSync } from "node:fs"; // Istasyon haritasi: 60 parcali bir yukselti egrisi, belirlenimci uretim. const nokta = Array.from({ length: 60 }, (_, i) => `${(i * 8).toFixed(0)} ${(120 - 40 * Math.sin(i / 6) - i / 3).toFixed(2)}`); writeFileSync("varlik/istasyon.svg", `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 480 160">\n` + `<path fill="none" stroke="#1b3a2f" stroke-width="2" d="M${nokta.join(" L")}"/>\n` + nokta.filter((_, i) => i % 6 === 0).map((n) => `<circle cx="${n.split(" ")[0]}" cy="${n.split(" ")[1]}" r="3"/>`).join("\n") + `\n</svg>\n`); // Yazi tipi govdesi yerine ayni boyutta belirlenimci bir ikili dosya konur. writeFileSync("varlik/olcum.woff2", Buffer.alloc(4096, 0x77)); ' cat > bicem/istasyon.css <<'EOF' @font-face { font-family: "Olcum"; src: url("../varlik/olcum.woff2") format("woff2"); font-display: swap; } .olcum li::before { content: ""; background-image: url("../varlik/simge.svg"); width: 16px; height: 16px; } .istasyon-harita { background-image: url("../varlik/istasyon.svg"); min-height: 160px; } EOF cat > kaynak/varlik-baglari.js <<'EOF' export const simgeUrl = new URL("../varlik/simge.svg", import.meta.url); export const haritaUrl = new URL("../varlik/istasyon.svg", import.meta.url); EOF printf 'User-agent: *\nDisallow: /panel/\n' > genel/robots.txt printf '{ "istasyon": "kuzey-yamac", "yukseklik_m": 1840 }\n' > genel/istasyon.json
Boru Hattı
Boru hattı dört adımdır: bağları tara, her varlık için gömme kararı ver, referansı çıktı adresiyle değiştir, kaynak ile çıktı arasındaki eşlemeyi bir bildirime yaz.
// varlik.mjs — varlik boru hatti: tarama, gomme karari, yeniden yazma, bildirim uretimi. import { readFileSync, writeFileSync, mkdirSync, cpSync, statSync, rmSync } from "node:fs"; import { basename, dirname, join, relative } from "node:path"; const GOMME_ESIGI = 1024; // bayt const CIKTI = "cikti"; const TUR = { ".svg": ["image/svg+xml", "metin"], ".css": ["text/css", "metin"], ".woff2": ["font/woff2", "ikili"], ".png": ["image/png", "ikili"] }; const uzanti = (y) => y.slice(y.lastIndexOf(".")); const KAYNAKLAR = ["bicem/istasyon.css", "kaynak/varlik-baglari.js"]; const BAG = [ /url\(\s*["']?([^"')]+)["']?\s*\)/g, // bicem dosyasindaki bag /new URL\(\s*["']([^"']+)["']\s*,\s*import\.meta\.url\s*\)/g, // koddaki bag ]; function veriUrl(yol) { // metin varlikta yuzde kodlama, ikilide base64 const [mime, sinif] = TUR[uzanti(yol)], govde = readFileSync(yol); return sinif === "metin" ? "data:" + mime + "," + encodeURIComponent(govde.toString("utf8")).replace(/%20/g, " ") : "data:" + mime + ";base64," + govde.toString("base64"); } rmSync(CIKTI, { recursive: true, force: true }); mkdirSync(join(CIKTI, "varlik"), { recursive: true }); const varliklar = new Map(); // yol -> { boyut, referans, gomulu, hedef, ciktiBoyut } const bildirim = {}; for (const kaynakYolu of KAYNAKLAR) { let metin = readFileSync(kaynakYolu, "utf8"); for (const kalip of BAG) { metin = metin.replace(kalip, (tam, bag) => { const yol = relative(".", join(dirname(kaynakYolu), bag)); if (!varliklar.has(yol)) { const boyut = statSync(yol).size; const gomulu = boyut <= GOMME_ESIGI; const hedef = gomulu ? veriUrl(yol) : "/varlik/" + basename(yol); if (!gomulu) cpSync(yol, join(CIKTI, hedef)); varliklar.set(yol, { boyut, referans: 0, gomulu, hedef, ciktiBoyut: gomulu ? Buffer.byteLength(hedef) : boyut }); } const v = varliklar.get(yol); v.referans++; return tam.startsWith("url") ? 'url("' + v.hedef + '")' : 'new URL("' + v.hedef + '", import.meta.url)'; }); } const ciktiYolu = join(CIKTI, basename(kaynakYolu)); writeFileSync(ciktiYolu, metin); bildirim["/" + basename(kaynakYolu)] = { kaynak: kaynakYolu, boyut: statSync(ciktiYolu).size }; } for (const [yol, v] of varliklar) if (!v.gomulu) bildirim[v.hedef] = { kaynak: yol, boyut: v.boyut }; cpSync("genel", CIKTI, { recursive: true }); // dogrudan kopyalanan dizin writeFileSync(join(CIKTI, "varlik-bildirimi.json"), JSON.stringify(bildirim, null, 2) + "\n"); const b64 = (yol) => Math.ceil(statSync(yol).size / 3) * 4; console.log("varlik".padEnd(20) + "boyut".padStart(6) + " ref karar cikti gomulseydi"); for (const [yol, v] of varliklar) console.log(basename(yol).padEnd(20) + String(v.boyut).padStart(6) + " " + v.referans + " " + (v.gomulu ? "gomuldu " : "ayri dosya") + String(v.ciktiBoyut).padStart(7) + String(b64(yol)).padStart(12)); console.log("-- yeniden yazilmis bicem dosyasi (uzun degerler kisaltildi) --"); for (const s of readFileSync(join(CIKTI, "istasyon.css"), "utf8").trimEnd().split("\n")) console.log(" " + (s.length > 64 ? s.slice(0, 64) + "[...]" : s)); console.log("-- bildirim anahtarlari --"); for (const [url, k] of Object.entries(bildirim)) console.log(" " + url.padEnd(24) + String(k.boyut).padStart(5) + " B <- " + k.kaynak); const ayri = [...varliklar.values()].filter((v) => !v.gomulu); console.log("-- toplam --"); console.log(" bicem dosyasi : " + bildirim["/istasyon.css"].boyut + " B (kaynakta " + statSync("bicem/istasyon.css").size + " B)"); console.log(" ayri varlik : " + ayri.length + " dosya, " + ayri.reduce((t, v) => t + v.boyut, 0) + " B"); console.log(" dogrudan kopya : robots.txt, istasyon.json (ad ve icerik degismedi)");
$ node varlik.mjs
varlik boyut ref karar cikti gomulseydi
olcum.woff2 4096 1 ayri dosya 4096 5464
simge.svg 168 2 gomuldu 275 224
istasyon.svg 1162 2 ayri dosya 1162 1552
-- yeniden yazilmis bicem dosyasi (uzun degerler kisaltildi) --
@font-face {
font-family: "Olcum";
src: url("/varlik/olcum.woff2") format("woff2");
font-display: swap;
}
.olcum li::before {
content: "";
background-image: url("data:image/svg+xml,%3Csvg xmlns%3D%22ht[...]
width: 16px;
height: 16px;
}
.istasyon-harita {
background-image: url("/varlik/istasyon.svg");
min-height: 160px;
}
-- bildirim anahtarlari --
/istasyon.css 575 B <- bicem/istasyon.css
/varlik-baglari.js 404 B <- kaynak/varlik-baglari.js
/varlik/olcum.woff2 4096 B <- varlik/olcum.woff2
/varlik/istasyon.svg 1162 B <- varlik/istasyon.svg
-- toplam --
bicem dosyasi : 575 B (kaynakta 323 B)
ayri varlik : 2 dosya, 5258 B
dogrudan kopya : robots.txt, istasyon.json (ad ve icerik degismedi)
Yeniden yazılmış biçem dosyasında üç bağın üçü de değişmiştir: ikisi kök göreli bir adrese,
biri gövdenin içine. Kaynaktaki ../varlik/ göreli yolu çıktıda hiç görünmez — çıktı
düzeni kaynak düzeninden bağımsızdır.
Referans sütunu bir ayrıntıyı açığa çıkarır: simge ve harita, hem biçem dosyasından hem koddan bağlanmıştır. Ayrı dosya olarak yazılan harita bir kez kopyalanır ve iki referans aynı adrese bakar. Gömülen simge ise her referansta yeniden yazılır; 168 baytlık dosya iki çıktıda toplam 550 bayt tutar.
Gömme Eşiği
Gömme kararı bir eşikle verilir ve eşik iki karşıt maliyeti dengeler.
Gömmenin kazancı bir istektir. Ayrı dosya olarak yazılan her varlık, taşındığı baytın yanında bir de istek gecikmesi getirir. Küçük varlıklarda bu gecikme, dosyanın kendisinden pahalıdır.
Gömmenin bedeli üç kalemdir. Birincisi kodlama genişlemesidir: ikili veri base64’e çevrildiğinde yaklaşık üçte bir büyür — 4096 baytlık yazı tipi gövdesi 5464 karaktere çıkar. Metin varlıklarında yüzde kodlaması kullanılabilir, ama her zaman daha ucuz değildir: örnekteki simge yüzde kodlamasıyla 275, base64 ile 224 bayttır, çünkü kaçırılması gereken karakter oranı yüksektir. İkincisi çoğaltmadır: gömülen varlık, kendisini bağlayan her çıktıda ayrı ayrı bulunur. Üçüncüsü önbelleklemedir — gömülen varlığın ayrı bir adresi olmadığı için ayrı önbelleklenemez; bağlandığı dosya her değiştiğinde onunla birlikte yeniden iner.
Bu üçüncü kalem, eşiğin neden küçük tutulduğunu açıklar. Nadiren değişen bir görselin, sık değişen bir biçem dosyasının içine gömülmesi, o görseli her yayında yeniden indirtir. Sonraki ders bu bağı ölçülebilir hâle getirecek.
Varlık Sınıflarına Özgü Kararlar
Eşik tek başına yeterli bir politika değildir; varlık sınıfının kendi kısıtları vardır.
Yazı tipleri boyutlarına bakılmaksızın ayrı dosya olarak yazılır. Bir yazı tipi
gövdesi birden çok sayfada ortaktır ve uzun ömürle önbelleklenir; gömülmesi bu paylaşımı
yok eder. CSS ile Görsel Sunum kursundaki yazı tipi yükleme dersinde kurulan iki ayar
burada da geçerlidir: görüntüleme davranışı font-display ile bildirilir ve gövde, belge
içinde erken bir yükleme ipucuyla istenebilir. Derleme tarafının ekleyebileceği üçüncü şey
alt küme çıkarmadır — gövdeden yalnız kullanılan karakter aralıklarının bırakılması,
dosyayı belirgin biçimde küçültür ve karar, hedeflenen dil kümesine bağlıdır.
Görseller iki ek üstveri taşır. Birincisi ölçülerdir: genişlik ve yükseklik derleme sırasında okunup işaretlemeye yazıldığında, Web’in Temelleri kursunda tanımlanan içerik kayması önlenir. İkincisi türevlerdir: Düzen Sistemleri ve Duyarlı Tasarım kursundaki aday kümesi, aynı görselin birkaç genişlikte üretilmiş sürümlerini ister; bu sürümleri üreten yer boru hattıdır. Biçim seçimi ürün adıyla değil sınıfla yapılır — kayıpsız sıkıştırma isteyen çizimler ile kayıplı sıkıştırmaya uygun fotoğraflar ayrı ele alınır.
Biçem dosyaları hem varlık hem kaynaktır: içlerindeki bağlar taranır, kendileri de çıktıya yazılır. Bu yüzden boru hattında iki kez görünürler.
Varlık Bildirimi
Boru hattının son çıktısı bir eşlemedir: hangi kaynak dosyanın çıktıda hangi adrese karşılık geldiği. Bu dosyaya varlık bildirimi (asset manifest) denir ve derleme ile onun dışındaki her şey arasındaki sözleşmedir.
Bildirimi okuyan en az üç taraf vardır. Belge şablonu, biçem dosyasının ve giriş parçasının adresini oradan alır. Sunucuda oluşturan bir çalışma zamanı, ürettiği işaretlemeye aynı adresleri yazar. Çevrimdışı çalışan bir servis çalışanı, önceden önbelleğe alacağı dosya listesini oradan üretir — Tarayıcı ve Web Platformu kursundaki önbellek stratejileri dersi bu listeyi elle yazmanın kırılganlığını göstermişti; bildirim, listeyi derlemenin üretmesini sağlar.
Ad benzerliği üç ayrı dosyayı karıştırmaya elverişlidir. Paket bildirimi bağımlılıkları ve paket üstverisini taşır; uygulama bildirimi kurulabilirlik bilgisini taşır; varlık bildirimi ise yalnız kaynak–çıktı eşlemesidir. Üçü farklı zamanlarda, farklı taraflarca okunur.
Özet
- Varlıklar çizgeye iki standart bağ üzerinden girer: biçem dosyasındaki
url()bildirimi ve koddakinew URL(...)ifadesi; çalışma zamanında birleştirilen adresler boru hattına giremez. - Bağlanan varlıklar işlenen akıştan, adı sabitlenmiş dosyalar doğrudan kopyalanan akıştan geçer; ölçüt, adresi derlemenin belirleyip belirleyemeyeceğidir.
- Boru hattı dört adımdır: bağları tara, gömme kararı ver, referansı çıktı adresiyle değiştir, eşlemeyi bildirime yaz.
- Gömme bir istek kazandırır; karşılığında kodlama genişlemesi, her referansta çoğaltma ve ayrı önbelleklenememe getirir — 4096 baytlık gövde base64’te 5464 karaktere çıkar.
- Yazı tipleri boyutundan bağımsız ayrı dosya olarak yazılır; görseller ölçü üstverisi ve genişlik türevleriyle birlikte üretilir.
- Varlık bildirimi, belge şablonu, sunucu çalışma zamanı ve servis çalışanı için ortak sözleşmedir.
Sonraki Adım
Çıktıdaki dosyalar şimdilik kaynaktaki adlarını taşıyor. Bu, iki sorunun ikisini de çözümsüz bırakır. Yayımlanan bir dosya uzun ömürle önbelleklenirse, içeriği değiştiğinde kullanıcıya eskisi verilmeye devam eder; kısa ömürle önbelleklenirse her ziyarette yeniden doğrulama yapılır ve önbelleğin değeri kaybolur. İki uçtan da kaçınmanın yolu adı içeriğe bağlamaktır: dosyanın adı içeriğinin özetini taşıdığında, içerik değişmedikçe ad değişmez ve ad değişmedikçe önbellek geçerli kalır. Sonraki ders bu adları üretir, adların birbirine bağlı parçalarda nasıl zincirleme değiştiğini ölçer ve zinciri kıran düzeneği kurar.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.