İçeriğe geç
academia.sh

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 koddaki new 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.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat