PWA Nedir, Hangi Problemi Çözer?
Progressive Web App (PWA), tarayıcıda çalışan bir web uygulamasına, kurulabilirlik, çevrimdışı çalışma ve arka plan yetenekleri kazandıran bir yaklaşımdır. Ayrı bir teknoloji değil; mevcut web platformunun belirli API'lerini ve tarayıcı sözleşmelerini bir arada kullanmaktır.
Temel yapı taşları şunlardır:
- HTTPS (veya
localhost): Servis çalışanı ve diğer güçlü API'ler yalnızca güvenli bağlamda çalışır. - Web App Manifest: Uygulamanın adı, ikonları, başlangıç URL'i ve görüntüleme modu gibi meta veriler.
- Service Worker: Ağ isteklerini yakalayan, önbellekleme ve çevrimdışı davranışı yöneten arka plan betiği.
Bu rehber, orta seviye bir geliştiricinin sıfırdan çalışan bir PWA iskeleti kurmasını hedefler. Örnekler framework bağımsızdır; vanilla JavaScript ve standart tarayıcı API'leri kullanılır. Belirli bir tarayıcı sürümüne bağlı davranışlardan kaçınılır, çünkü PWA API'leri tarayıcılar arasında farklı hızlarda olgunlaşır.
Önkoşullar
- HTML, CSS ve JavaScript'e hâkimiyet.
- Uygulamanın
https://üzerinden (geliştirmedehttp://localhost) sunulması. - Basit bir statik sunucu veya mevcut bir web uygulaması.
- Tarayıcı geliştirici araçlarında Application sekmesini kullanabilme.
Adım 1: Web App Manifest Ekle
Manifest, tarayıcıya uygulamanın nasıl kurulacağını anlatır. Kök dizine manifest.webmanifest adında bir dosya koy:
{
"name": "Örnek PWA",
"short_name": "Örnek",
"start_url": "/",
"scope": "/",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#0d6efd",
"icons": [
{
"src": "/icons/icon-192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/icons/icon-512.png",
"sizes": "512x512",
"type": "image/png"
},
{
"src": "/icons/icon-512-maskable.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
}
]
}
HTML <head> içinde manifest'i bağla:
<link rel="manifest" href="/manifest.webmanifest">
<meta name="theme-color" content="#0d6efd">
Beklenen davranış: Tarayıcı manifest'i ayrıştırır; kurulum koşulları sağlandığında adres çubuğunda veya menüde "Yükle" seçeneği görünür. display: "standalone" seçildiğinde kurulu uygulama tarayıcı arayüzü olmadan açılır.
Dikkat: start_url ve scope değerlerinin uygulamanın gerçek yollarıyla uyumlu olması gerekir. scope dışına çıkan bağlantılar PWA penceresinden çıkar.
Adım 2: Service Worker'ı Kaydet
Service worker, ana sayfadan ayrı bir dosyadır ve kendi kapsamı (scope) içindeki istekleri kontrol eder. Kök dizinde sw.js oluştur ve sayfada kaydettir:
<script>
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/sw.js')
.then((reg) => {
console.log('Service worker kayıtlı, kapsam:', reg.scope);
})
.catch((err) => {
console.error('Kayıt başarısız:', err);
});
});
}
</script>
Beklenen davranış: İlk yüklemede service worker kurulur ve etkinleşir. DevTools'un Application → Service Workers bölümünde durumu görünür. İlk ziyarette sayfa henüz service worker tarafından kontrol edilmez; kontrol bir sonraki yüklemede veya clients.claim() çağrısıyla devreye girer.
Yaygın hata: sw.js dosyasını /js/sw.js gibi bir alt dizine koymak, kapsamı o dizinle sınırlar. Kök kapsam istiyorsan dosya kökte olmalı ya da Service-Worker-Allowed başlığı ayarlanmalıdır.
Adım 3: Önbellekleme Stratejisi Kur
Service worker'ın install ve fetch olaylarını ele alarak temel bir "önce önbellek, sonra ağ" stratejisi kurabilirsin:
const CACHE = 'app-v1';
const PRECACHE = [
'/',
'/index.html',
'/styles.css',
'/app.js',
'/manifest.webmanifest'
];
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE).then((cache) => cache.addAll(PRECACHE))
);
});
self.addEventListener('activate', (event) => {
event.waitUntil(
caches.keys().then((keys) =>
Promise.all(
keys.filter((k) => k !== CACHE).map((k) => caches.delete(k))
)
)
);
});
self.addEventListener('fetch', (event) => {
if (event.request.method !== 'GET') return;
event.respondWith(
caches.match(event.request).then((cached) => {
return cached || fetch(event.request);
})
);
});
Beklenen davranış: İlk kurulumda listedeki dosyalar önbelleğe alınır. Sonraki ziyaretlerde bu dosyalar ağdan değil önbellekten gelir; çevrimdışıyken de açılır.
Önemli noktalar:
CACHEadını içerik değiştiğinde artır (örneğinapp-v2).activateadımı eski önbellekleri temizler.cache.addAlliçindeki herhangi bir dosya 404 dönerseinstallbaşarısız olur ve service worker etkinleşmez. Listeyi gerçekten var olan dosyalarla sınırlı tut.- Yalnızca
GETisteklerini ele almak, form gönderimi gibi isteklerin yanlışlıkla önbelleğe alınmasını önler.
Adım 4: Güncelleme Akışını Yönet
Service worker dosyası bayt düzeyinde değiştiğinde tarayıcı yeni bir sürüm kurar. Ancak yeni sürüm, eski sekmeler kapanana kadar "beklemede" kalabilir. Kullanıcıya güncelleme bildirimi göstermek için:
navigator.serviceWorker.register('/sw.js').then((reg) => {
reg.addEventListener('updatefound', () => {
const yeni = reg.installing;
if (!yeni) return;
yeni.addEventListener('statechange', () => {
if (yeni.state === 'installed' && navigator.serviceWorker.controller) {
// Yeni sürüm hazır; kullanıcıya bildir ve sayfayı yenile
console.log('Yeni sürüm hazır. Yeniden yükleyin.');
}
});
});
});
Beklenen davranış: Yeni service worker kurulduğunda updatefound tetiklenir; installed durumuna geldiğinde ve sayfa hâlihazırda bir service worker tarafından kontrol ediliyorsa güncelleme mevcuttur.
Yaygın hata: Service worker'ı skipWaiting ile koşulsuz etkinleştirmek, açık sekmelerdeki önbellek ile yeni kod arasında tutarsızlığa yol açabilir. Bunu kullanıcı onayına bağlamak daha güvenlidir.
Adım 5: Çevrimdışı Deneyimi Doğrula
DevTools'ta:
- Application → Service Workers altında "Offline" kutusunu işaretle.
- Sayfayı yenile.
- Önceden önbelleğe alınmış kaynaklar yüklenmeli; önbellekte olmayan istekler başarısız olmalı.
Önbellekte olmayan bir rota için kullanıcıya özel bir çevrimdışı sayfası göstermek istersen, fetch işleyicisinde hata yakalayabilirsin:
event.respondWith(
caches.match(event.request).then((cached) => {
if (cached) return cached;
return fetch(event.request).catch(() => caches.match('/offline.html'));
})
);
/offline.html dosyasını PRECACHE listesine eklemeyi unutma.
Güvenlik ve Gizlilik Etkileri
- HTTPS zorunluluğu tesadüfi değildir: Service worker, ağ trafiğini programatik olarak ele alabildiği için araya giren bir aktörün tüm istekleri ele geçirmesini engellemek amacıyla yalnızca güvenli bağlamda çalışır.
- Önbellek kapsamı: Önbelleğe alınan yanıtlar kullanıcının diskinde kalır. Kişisel veri içeren yanıtları önbelleğe almaktan kaçın; oturum belirteci içeren istekleri
fetchişleyicisinde hariç tut. - Kapsam sınırı: Service worker yalnızca kayıtlı olduğu kapsam içindeki istekleri görebilir. Kapsamı gereğinden geniş tutmak saldırı yüzeyini artırır.
- Güncelleme güveni: Service worker dosyası, kaynağından sunulduğu sürece tarayıcı tarafından önbelleğe alınmaz (varsayılan olarak
no-cachebenzeri davranış). Sunucu tarafında bu dosyaya uzun ömürlüCache-Controlbaşlığı eklemek güncellemeleri geciktirir.
Yaygın Hatalar Özeti
| Hata | Sonuç |
|---|---|
sw.js dosyasını alt dizine koymak |
Kapsam daralır, kök istekler kontrol edilmez |
PRECACHE listesinde olmayan dosya |
install başarısız olur, service worker etkinleşmez |
CACHE adını hiç değiştirmemek |
Eski içerik sonsuza kadar önbellekte kalır |
| Tüm istekleri önbelleğe almak | Dinamik/kimlik doğrulamalı yanıtlar yanlış sunulur |
skipWaiting'i koşulsuz kullanmak |
Açık sekmelerde tutarsız durum |
Sonraki Adımlar
Temel iskelet çalıştıktan sonra bakılacak başlıklar:
- Push bildirimleri:
PushManagervenotificationclickolayı. - Background Sync: Çevrimdışıyken başarısız olan istekleri bağlantı gelince yeniden denemek.
- Workbox gibi kütüphanelerle önbellekleme stratejilerini (stale-while-revalidate, network-first) hazır şablonlarla kurmak.
- Lighthouse ile kurulabilirlik ve performans denetimi.
PWA'lar tek seferde mükemmel olmaz; önbellek stratejisini uygulamanın gerçek trafik desenine göre ayarlamak gerekir. Küçük başla, her adımı DevTools üzerinden doğrula, önbellek sürümünü disiplinli biçimde yönet.