İçeriğe geç
academia.sh

Ders 19 / 24

Aşamalı Web Uygulamaları

Sayfayı kurulabilir bir uygulamaya dönüştüren parçalar; uygulama bildiriminin alanları ve iç tutarlılığı, kurulabilirlik ölçütleri, pencere kipinin getirdiği sorumluluklar, izin modeli ve çevrimdışı gönderim kuyruğu.

İçindekiler

Önceki ders sayfayı ağdan bağımsızlaştırdı: kaynaklar yerelde, kararlar tanımlı. Ortaya çıkan şey bir sayfadan fazlasıdır ve kullanıcıya da öyle görünmesi gerekir. Kuzey Yamaç sayfasını sahada kullanan kişi onu her seferinde adres yazarak açmak istemez; kendi penceresinde, kendi simgesiyle açılmasını bekler.

Bu ders üç parçayı ele alır: sayfanın kendini tanıttığı bildirim, kurulabilirliğin koşulları ve arka plandan gelen bildirimlerin izin modeli. Bunlara aşamalı web uygulaması (progressive web app) yaklaşımı denir; adındaki aşamalılık, sayfanın desteklenmeyen ortamda sıradan bir sayfa olarak çalışmayı sürdürmesini anlatır.

Uygulama Bildirimi

Sayfa, kendini bir uygulama olarak tanıtan bir JSON dosyasına bağlanır. Dosya, işletim sisteminin uygulamayı listelemek ve pencereyi açmak için ihtiyaç duyduğu bilgileri taşır: görünen ad, kısa ad, açılışta gidilecek adres, uygulamanın sayılacağı yol kapsamı, pencere kipi, simgeler ve renkler.

Alanların çoğu biçimsel olarak bağımsızdır, ama aralarında sessiz tutarlılık koşulları vardır. Bunlar denetlenebilir.

// bildirim-denetimi.mjs — uygulama bildiriminin ic tutarliligini sinar
const ZORUNLU = ["name", "start_url", "icons", "display"];
const GEREKEN_BOYUTLAR = [192, 512];

function denetle(bildirim) {
  const bulgular = [];

  for (const alan of ZORUNLU)
    if (!(alan in bildirim)) bulgular.push(`eksik alan: ${alan}`);

  const kapsam = bildirim.scope ?? "/";
  if (bildirim.start_url && !bildirim.start_url.startsWith(kapsam))
    bulgular.push(`start_url (${bildirim.start_url}) kapsamin (${kapsam}) disinda`);

  const simgeler = bildirim.icons ?? [];
  const boyutlar = new Set(
    simgeler.flatMap((s) => (s.sizes ?? "").split(" ")).map((b) => Number(b.split("x")[0])));
  for (const gereken of GEREKEN_BOYUTLAR)
    if (![...boyutlar].some((b) => b >= gereken))
      bulgular.push(`${gereken}x${gereken} ve uzeri simge yok`);

  if (!simgeler.some((s) => (s.purpose ?? "").split(" ").includes("maskable")))
    bulgular.push("maskable amacli simge yok: kirpilan yuvalarda kenarlar kesilir");

  if (bildirim.display && !["fullscreen", "standalone", "minimal-ui", "browser"].includes(bildirim.display))
    bulgular.push(`taninmayan display degeri: ${bildirim.display}`);

  return bulgular;
}

const bildirimler = {
  "kuzey-yamac.webmanifest": {
    name: "Kuzey Yamaç Ölçüm İstasyonu",
    short_name: "Kuzey Yamaç",
    start_url: "/istasyon/kuzey-yamac/",
    scope: "/istasyon/kuzey-yamac/",
    display: "standalone",
    background_color: "#0f1115",
    theme_color: "#0f1115",
    icons: [
      { src: "/simge-192.png", sizes: "192x192", type: "image/png" },
      { src: "/simge-512.png", sizes: "512x512", type: "image/png" },
      { src: "/simge-maskeli.png", sizes: "512x512", type: "image/png", purpose: "maskable" },
    ],
  },
  "eksik.webmanifest": {
    short_name: "Kuzey Yamaç",
    start_url: "/panel/",
    scope: "/istasyon/",
    display: "app",
    icons: [{ src: "/simge-64.png", sizes: "64x64", type: "image/png" }],
  },
};

for (const [ad, bildirim] of Object.entries(bildirimler)) {
  const bulgular = denetle(bildirim);
  console.log(`${ad}: ${bulgular.length === 0 ? "tutarli" : bulgular.length + " bulgu"}`);
  for (const b of bulgular) console.log("  - " + b);
}
kuzey-yamac.webmanifest: tutarli
eksik.webmanifest: 6 bulgu
  - eksik alan: name
  - start_url (/panel/) kapsamin (/istasyon/) disinda
  - 192x192 ve uzeri simge yok
  - 512x512 ve uzeri simge yok
  - maskable amacli simge yok: kirpilan yuvalarda kenarlar kesilir
  - taninmayan display degeri: app

İkinci bulgu en sinsi olanıdır. Bildirimdeki kapsam, uygulamanın sınırını çizer: kapsam dışındaki bir adres uygulama penceresinde değil tarayıcıda açılır. Açılış adresi kapsam dışındaysa uygulama kurulur, simgesi görünür, ama her açılışta kendi penceresinden çıkar. Bu, servis çalışanının kapsamıyla da hizalanmalıdır; iki kapsam ayrıştığında uygulama penceresinde açılan bir adres çevrimdışı desteğinden yoksun kalabilir.

Maskeli simge bulgusu görsel bir ayrıntı gibi görünür ve değildir. Sistem simgeyi kendi biçimine kırpar; kırpmayı hesaba katmayan bir simgenin kenarları kesilir. Ayrı bir amaç etiketiyle işaretlenen simge, çevresinde güvenli bir boşluk bırakılmış olanıdır.

Kurulabilirliğin Ölçütleri

Kurulabilirlik bildirimin varlığıyla kendiliğinden gelmez. Ortak çekirdek şunlardır: sayfanın güvenli bir kökenden sunulması, geçerli bir bildirimin bağlanmış olması, yeterli boyutta simge sunulması ve genellikle istekleri karşılayan bir servis çalışanının kayıtlı olması. Bunların ötesindeki ölçütler tarayıcıdan tarayıcıya değişir ve zamanla değişebilir; tek doğru yaklaşım, kurulum akışını özellik saptamayla kurmaktır.

Kurulum önerisinin ne zaman gösterileceği sayfanın kararı değildir. Tarayıcı uygun gördüğü anda bir olay yayar; sayfa bu olayı yakalayıp erteleyebilir ve kendi arayüzündeki bir düğmeye bağlayabilir. Kurulum penceresini kullanıcı etkileşimi olmadan açmak mümkün değildir. Olay hiç gelmeyebilir de: uygulama zaten kuruluysa ya da ortam kurulumu desteklemiyorsa. Kurulum düğmesi bu yüzden koşullu gösterilir.

Pencere Kipi ve Getirdiği Sorumluluk

Bağımsız pencere kipinde tarayıcı arayüzü görünmez: adres çubuğu, geri düğmesi, yenileme düğmesi yoktur. Sayfanın gizli olarak dayandığı her tarayıcı yeteneği artık sayfanın kendi sorumluluğudur.

Üç şey doğrudan etkilenir. Geri gezinme arayüzde açıkça sunulmalıdır; kullanıcı geri dönemediği bir ekranda sıkışır. Yenileme yolu bırakılmalıdır; çevrimdışı yedek sayfa gösterildiğinde kullanıcının yeniden denemek için bir düğmesi olmalıdır. Adresin paylaşılması için bir yol gerekir; adres çubuğu olmadığında bulunulan ekranın adresini kopyalamanın başka yolu yoktur.

Sayfanın hangi kipte açıldığı bir ortam sorgusuyla okunabilir; bu, aynı belgenin tarayıcı sekmesinde ve uygulama penceresinde farklı gezinme ögeleri göstermesini sağlar. Kip bildirimi ayrıca bir yedek zinciri gibi çalışır: istenen kip desteklenmiyorsa daha az ayrıcalıklı kipe düşülür, en sonunda sıradan sekme kalır.

Bildirimler ve İzin Modeli

Bildirim gönderme yetkisi kullanıcının açık onayına bağlıdır ve izin üç durumdan birinde bulunur: sorulmamış, verilmiş, reddedilmiş. Reddedilmiş durum kalıcıdır; sayfa aynı izni yeniden soramaz, kullanıcı ayarlardan geri almadıkça.

Bu kalıcılık tek bir kural doğurur: izin, kullanıcının o izne ihtiyaç duyan bir eylemi başlattığı anda istenir. Sayfa açılır açılmaz sorulan izin çoğunlukla reddedilir ve o reddetme kalıcıdır; ileride gerçekten gerektiğinde sorma hakkı yakılmış olur. Doğru sıra, önce arayüzde ne için kullanılacağını anlatan bir adım göstermek, sonra tarayıcının izin penceresini açmaktır.

Uzaktan bildirim ayrıca bir sunucu tarafı gerektirir: sayfanın oluşturduğu abonelik sunucuya kaydedilir, sunucu iletiyi bir aracıya gönderir, aracı da servis çalışanını uyandırır. Abonelik kalıcı değildir; tarayıcı yenileyebilir ve sunucudaki kayıt eskiyebilir. Bu yüzden bildirim hiçbir zaman tek iletişim kanalı sayılmaz; uygulama açıldığında aynı bilgi arayüzde de bulunmalıdır.

Çevrimdışı Gönderim ve Yinelemeye Dayanıklılık

Çevrimdışı okuma önceki dersin konusuydu; çevrimdışı yazma ayrı bir sorundur. Sahada alınan ölçüm bağlantı yokken gönderilemez; kaybolmaması için yerel bir kuyruğa yazılır ve bağlantı geldiğinde boşaltılır. Bazı ortamlarda bu boşaltmayı tarayıcı, sayfa kapalıyken bile yapabilir; desteği koşulludur ve saptanarak kullanılır.

Kuyruğun kendisi yeni bir hata sınıfı üretir. Sunucu kaydı yazdıktan sonra yanıt yolda kaybolursa, istemci gönderimi başarısız sayar ve yeniden dener.

// kuyruk.mjs — cevrimdisi gonderim kuyrugu ve yinelemeye dayaniklilik
// Senaryo: kayit sunucuya yazilir, ama yanit istemciye ulasmadan baglanti kopar.
// Istemci basarisiz saydigi gonderimi yeniden dener.

function sunucu() {
  const kayitlar = [];
  const gorulenAnahtarlar = new Set();
  return {
    kayitlar,
    yaz(govde, anahtar) {
      if (anahtar && gorulenAnahtarlar.has(anahtar)) return "yinelenen istek, yok sayildi";
      if (anahtar) gorulenAnahtarlar.add(anahtar);
      kayitlar.push(govde);
      return "yazildi";
    },
  };
}

// Kuyrugu bosaltir. yanitKaybi[i] true ise i. denemede yanit istemciye ulasmaz.
function bosalt(kuyruk, s, yanitKaybi) {
  let deneme = 0;
  while (kuyruk.length > 0 && deneme < 5) {
    const is = kuyruk[0];
    const sonuc = s.yaz(is.govde, is.anahtar);
    const kayboldu = yanitKaybi[deneme] === true;
    deneme += 1;
    console.log(`  deneme ${deneme}: sunucu "${sonuc}"` +
      (kayboldu ? " → yanit kayboldu, yeniden denenecek" : " → yanit ulasti"));
    if (!kayboldu) kuyruk.shift();
  }
}

const olcum = { istasyon: "kuzey-yamac", sicaklik: -4.2 };

for (const anahtar of [null, "gonderim-9f3c"]) {
  console.log(anahtar ? `yinelemeye dayanikli anahtar: ${anahtar}` : "anahtarsiz gonderim:");
  const s = sunucu();
  const kuyruk = [{ govde: olcum, anahtar }];
  bosalt(kuyruk, s, [true, false]); // ilk denemede yanit kaybi
  console.log(`  sunucudaki kayit sayisi: ${s.kayitlar.length}\n`);
}
anahtarsiz gonderim:
  deneme 1: sunucu "yazildi" → yanit kayboldu, yeniden denenecek
  deneme 2: sunucu "yazildi" → yanit ulasti
  sunucudaki kayit sayisi: 2

yinelemeye dayanikli anahtar: gonderim-9f3c
  deneme 1: sunucu "yazildi" → yanit kayboldu, yeniden denenecek
  deneme 2: sunucu "yinelenen istek, yok sayildi" → yanit ulasti
  sunucudaki kayit sayisi: 1

Aynı ölçüm iki kez kaydedilmiştir ve istemci tarafında hiçbir hata görünmemiştir. Ayrım, gönderimle birlikte üretilen ve yeniden denemelerde değişmeyen bir anahtardır. Anahtar istemcide, gönderim kuyruğa alınırken üretilir; her denemede yeniden üretilirse hiçbir işe yaramaz. Sunucu tarafındaki karşılık, aynı anahtarı ikinci kez gören isteği yok sayıp başarı bildirmektir.

Kuyruğun kullanıcıya görünürlüğü de tasarımın parçasıdır. Gönderilmemiş ölçümlerin sayısı arayüzde durmalı, gönderim tamamlandığında kaybolmalıdır; kullanıcının sakladığını sandığı bir verinin sessizce kuyrukta beklemesi, çevrimdışı desteğin en pahalı hatasıdır.

Özet

  • Uygulama bildirimi, sayfayı işletim sistemine tanıtan JSON dosyasıdır; alanları arasında denetlenebilir tutarlılık koşulları vardır.
  • Açılış adresi bildirimin kapsamı içinde olmalıdır; dışında kalırsa uygulama her açılışta kendi penceresinden çıkar.
  • Kurulabilirlik ölçütleri güvenli köken, geçerli bildirim ve genellikle kayıtlı bir servis çalışanını içerir; ötesi ortama göre değişir ve özellik saptamayla ele alınır.
  • Bağımsız pencere kipinde geri gezinme, yenileme ve adres paylaşımı sayfanın kendi sorumluluğuna geçer.
  • İzin reddi kalıcıdır; izin, kullanıcının o yeteneğe ihtiyaç duyan bir eylemi başlattığı anda istenir.
  • Çevrimdışı gönderim kuyruğu yeniden deneme üretir; gönderim başına değişmeyen bir anahtar olmadan aynı kayıt birden çok kez yazılır.

Sonraki Adım

Buraya kadar kurulan katmanlar — belge ağacı, olay yolu, kapsüllenmiş bileşenler, araya giren çalışan, önbellek kararları — birlikte çalıştığında görünmez olurlar. Bir şey ters gittiğinde ise sorunun hangi katmandan geldiği metne bakarak anlaşılmaz: rozetin rengi yanlış olabilir çünkü bir biçem kuralı beklenmedik biçimde kazanmıştır, ya da ölçüm eski olabilir çünkü istek önbellekten karşılanmıştır. Bu iki tanı farklı sorulara ve farklı ölçülere ihtiyaç duyar. Sonraki konu, tarayıcının sunduğu tanı araçlarını bu sorular üzerinden ele alır ve ilk ders en yakınından başlar: bir ögenin gerçekte hangi değerlerle çizildiğini okumaktan.

İ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