---
title: 'Aşamalı Web Uygulamaları'
source: 'https://academia.sh/tr/kurslar/tarayici-platformu/asamali-web-uygulamalari'
course: 'Tarayıcı ve Web Platformu'
language: tr
updated: '2026-08-17T18:09:12+00:00'
license: 'CC BY-SA 4.0'
---

# 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.

Ö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.

```js
// 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.

```js
// 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.
