Aşamalı Web Uygulamaları (PWA'lar)

Aşamalı Web Uygulaması (PWA), uygulamaya benzer bir deneyim sunmak için modern tarayıcı özelliklerini kullanan bir web uygulamasıdır. PWA'lar güvenilir, hızlı ve ilgi çekicidir; web erişimini yerel mobil uygulamaların yetenekleriyle birleştirir.
Giriş
Aşamalı Web Uygulaması (PWA), uygulamaya benzer bir deneyim sunmak için modern tarayıcı özelliklerini kullanan bir web uygulamasıdır. PWA'lar güvenilir, hızlı ve ilgi çekicidir; web erişimini yerel mobil uygulamaların yetenekleriyle birleştirir.
PWA'lar temel bir sorunu çözüyor: Kullanıcılar web'in anında kullanılabilirliğini istiyor (kurulum yok, bağlantı kurulabilir, her zaman güncel) ancak yerel uygulamaların performansını ve özelliklerini (çevrimdışı destek, anında bildirimler, ana ekran varlığı) bekliyorlar. PWA'lar her ikisini de sağlar.
Temel Özellikler
"Progresif Web Uygulaması" terimi, 2015 yılında Google'dan Alex Russell ve Frances Berriman tarafından icat edildi. Bir PWA'nın şu 10 özelliği karşılaması gerekir:
| # | Prensip | Neden Önemlidir? |
|---|---|---|
| 1 | Aşamalı — Herkes için işe yarar | Eski tarayıcılarda zarif bozulma |
| 2 | Duyarlı — Her ekrana sığar | Masaüstü, tablet, mobil |
| 3 | Bağlantıdan bağımsız — Çevrimdışı çalışır | Hizmet çalışanının önbelleğe alınması |
| 4 | Uygulama benzeri — Yerel uygulama hissi | Kabuk mimarisi, sorunsuz gezinme |
| 5 | Taze — Her zaman güncel | Hizmet çalışanı güncelleme yaşam döngüsü |
| 6 | Güvenli — HTTPS üzerinden sunulur | Servis çalışanları için gerekli olan kurcalamayı önler |
| 7 | Keşfedilebilir — SEO dostu | Arama motorları PWA'ları dizine ekler |
| 8 | Yeniden etkinleştirilebilir — Anlık bildirimler | Yerel uygulamalar gibi kullanıcılarla yeniden etkileşime geçin |
| 9 | Yüklenebilir — Ana ekrana ekle | Web uygulaması bildirimi |
| 10 | Bağlanabilir — URL aracılığıyla paylaşın | Uygulama mağazasına gerek yok |
PWA'nın Üç Sütunu
1. Servis Çalışanı — Motor
Hizmet çalışanı, web sayfasından ayrı olarak arka planda çalışan bir JavaScript dosyasıdır. Programlanabilir bir ağ proxy'si görevi görerek istekleri yakalar ve çevrimdışı işlevsellik, anında bildirimler ve arka planda senkronizasyon sağlar.
Hizmet Çalışanı Yaşam Döngüsü
Installing → Installed → Activating → Activated → Idle → Terminated
│ │
└─── (new version detected) ──────────────┘
└─── Waiting (until all tabs close) ─────┘
// service-worker.js
const CACHE_NAME = 'my-pwa-v2';
const ASSETS_TO_CACHE = [
'/',
'/index.html',
'/styles.css',
'/app.js',
'/offline.html',
'/icons/icon-192.png'
];
// Install event — cache static assets
self.addEventListener('install', event => {
event.waitUntil(
caches.open(CACHE_NAME).then(cache => {
console.log('Caching app shell');
return cache.addAll(ASSETS_TO_CACHE);
})
);
// Force new service worker to activate immediately
self.skipWaiting();
});
// Activate event — clean old caches
self.addEventListener('activate', event => {
event.waitUntil(
caches.keys().then(cacheNames => {
return Promise.all(
cacheNames
.filter(name => name !== CACHE_NAME)
.map(name => caches.delete(name))
);
})
);
// Take control of all open pages immediately
clients.claim();
});
// Fetch event — intercept network requests
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request).then(cachedResponse => {
// Return cached response if available
if (cachedResponse) {
return cachedResponse;
}
// Otherwise fetch from network
return fetch(event.request).then(networkResponse => {
// Don't cache non-GET or error responses
if (!event.request.url.includes('/api/')) {
return networkResponse;
}
// Cache API responses for offline use
const responseClone = networkResponse.clone();
caches.open(CACHE_NAME).then(cache => {
cache.put(event.request, responseClone);
});
return networkResponse;
}).catch(() => {
// Network failed — return offline fallback
if (event.request.mode === 'navigate') {
return caches.match('/offline.html');
}
return new Response('Offline', { status: 503 });
});
})
);
});
Önbelleğe Alma Stratejileri
| Strateji | Açıklama | Kullanım Örneği |
|---|---|---|
| Önce Önbellek | Önbelleği kontrol edin, ağa geri dönün | Statik varlıklar (CSS, JS, resimler) |
| Önce Ağ | Ağı deneyin, önbelleğe geri dönün | API çağrıları, dinamik içerik |
| **Yeniden Doğrulama Sırasında Eski ** | Önbelleği döndür, arka planda güncelle | Haber akışları, blog gönderileri |
| Yalnızca Ağ | Her zaman ağdan getir | Hassas veriler (bankacılık) |
| Yalnızca Önbellek | Hiçbir zaman ağdan getirme | Uygulama kabuğu |
// Stale-while-revalidate strategy
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request).then(cachedResponse => {
const fetchPromise = fetch(event.request).then(networkResponse => {
caches.open(CACHE_NAME).then(cache => {
cache.put(event.request, networkResponse.clone());
});
return networkResponse;
}).catch(() => cachedResponse);
return cachedResponse || fetchPromise;
})
);
});
2. Web Uygulaması Bildirimi — Kimlik
Bildiri, PWA'nın yüklendiğinde nasıl görüneceğini kontrol eden bir JSON dosyasıdır:
{
"name": "My Progressive Web App",
"short_name": "MyPWA",
"description": "An amazing PWA that works offline",
"start_url": "/?source=pwa",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#3367D6",
"orientation": "portrait-primary",
"scope": "/",
"icons": [
{
"src": "/icons/icon-192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "any maskable"
},
{
"src": "/icons/icon-512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "any maskable"
}
],
"categories": ["productivity", "utilities"],
"lang": "en-US",
"dir": "ltr",
"prefer_related_applications": false
}
Açıklanan manifest özellikleri:
display: standalone— Tarayıcı kullanıcı arayüzü (adres çubuğu, sekmeler) olmadan açılır.display: fullscreen— Tam ekran, tarayıcı kromu yok.display: minimal-ui— Minimum tarayıcı kontrolü.scope: "/"— Hangi URL'lerin PWA'nın parçası olduğu.start_url— PWA başlatıldığında açılan sayfa.- Maskelenebilir simgeler — İşletim sistemi düzeyinde maskelemeyle çalışan uyarlanabilir simgeler (yuvarlak, sincap vb.).
3. HTTPS — Temel
Hizmet çalışanları güvenli bir bağlama ihtiyaç duyar. HTTPS aşağıdakiler için zorunludur:
- Servis çalışanı kaydı.
- Bildirimleri itin.
- Yükleme istemi (unload olayından önce).
# Redirect HTTP to HTTPS
# Nginx
server {
listen 80;
server_name example.com;
return 301 https://$server_name$request_uri;
}
Bir PWA Oluşturmak — Adım Adım
1. Uygulama Kabuğunu Oluşturun
Uygulama kabuğu, kullanıcı arayüzünü oluşturmak için gereken minimum HTML, CSS ve JavaScript'tir. İlk yüklemede önbelleğe alınır ve PWA'nın temeli olarak hizmet eder.
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="theme-color" content="#3367D6">
<link rel="manifest" href="/manifest.json">
<link rel="icon" href="/icons/icon-192.png" sizes="192x192">
<link rel="apple-touch-icon" href="/icons/icon-192.png">
<title>My PWA</title>
<link rel="stylesheet" href="/styles.css">
</head>
<body>
<header id="app-header">My PWA</header>
<main id="app-content"></main>
<footer id="app-footer">© 2026</footer>
<!-- Register service worker -->
<script>
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/service-worker.js')
.then(reg => console.log('SW registered:', reg.scope))
.catch(err => console.error('SW registration failed:', err));
});
}
</script>
<script src="/app.js"></script>
</body>
</html>
2. Çevrimdışı Desteği Uygulayın
// app.js — Fetch content with offline support
const CACHE_KEY = 'content-cache';
async function loadContent(url) {
try {
const response = await fetch(url);
const data = await response.json();
// Cache for offline
const cache = await caches.open(CACHE_KEY);
cache.put(url, new Response(JSON.stringify(data)));
renderContent(data);
} catch (error) {
// Offline — load from cache
const cached = await caches.match(url);
if (cached) {
const data = await cached.json();
renderContent(data);
showOfflineIndicator();
} else {
showOfflineMessage();
}
}
}
3. Anlık Bildirimler Ekleyin
// Request permission
async function requestNotificationPermission() {
const permission = await Notification.requestPermission();
if (permission === 'granted') {
// Get push subscription
const registration = await navigator.serviceWorker.ready;
const subscription = await registration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array(publicKey)
});
// Send subscription to server
await fetch('/api/push/subscribe', {
method: 'POST',
body: JSON.stringify(subscription)
});
}
}
// service-worker.js — Handle push events
self.addEventListener('push', event => {
const data = event.data.json();
const options = {
body: data.body,
icon: '/icons/icon-192.png',
badge: '/icons/badge-72.png',
vibrate: [100, 50, 100],
data: { url: data.url },
actions: [
{ action: 'open', title: 'Open' },
{ action: 'close', title: 'Dismiss' }
]
};
event.waitUntil(
self.registration.showNotification(data.title, options)
);
});
self.addEventListener('notificationclick', event => {
event.notification.close();
if (event.action === 'open' || !event.action) {
clients.openWindow(event.notification.data.url);
}
});
PWA ve Yerel Uygulama
| Özellik | PWA | Yerel Uygulama |
|---|---|---|
| Dağıtım | URL (mağaza gerekmez) | App Store, Google Play |
| Sürtünmeyi kurun | Tek dokunuşla (tarayıcı istemi) | İndirme, yükleme, izinler |
| Dosya boyutu | KB'den MB'ye | 10MB - 500MB+ |
| Güncellemeler | Anında (hizmet çalışanı) | Uygulama mağazası incelemesi yoluyla |
| Çevrimdışı | Evet (hizmet çalışanı) | Evet (yerleşik) |
| Anlık bildirimler | Evet | Evet |
| Cihaz API'leri | Kamera, GPS, ivmeölçer, ödeme | Tüm cihaz API'lerine tam erişim |
| Bluetooth | Web Bluetooth (sınırlı) | Tam Bluetooth API'si |
| Arka plan görevleri | Arka planda senkronizasyon (sınırlı) | Tam arka planda yürütme |
| Performans | Yerele yakın (JavaScript/WebAssembly) | Yerel (derlenmiş kod) |
| SEO | Arama motorları tarafından indekslenebilir | Dizine eklenemez |
| Geliştirme maliyeti | Tek kod tabanı | iOS + Android (veya platformlar arası) |
| Kullanıcıyı elde tutma | Daha düşük (ayrılması daha kolay) | Daha yüksek (kurulu = kararlı) |
Yerel Yerine PWA'yı Ne Zaman Seçmelisiniz?
- İçerik odaklı uygulamalar — Haberler, bloglar, dergiler.
- E-ticaret — Alışveriş siteleri (Pinterest PWA: %40 daha fazla zaman harcandı).
- Yardımcı uygulamalar — Hesap makineleri, dönüştürücüler, hava durumu.
- Düşük etkileşimli uygulamalar — Kullanıcıların nadiren ziyaret ettiği uygulamalar.
- Gelişen pazarlar — Sınırlı depolama alanına ve verilere sahip kullanıcılar.
- Aşamalı geliştirme — Modern cihazlarda tam deneyim, eski cihazlarda işlevsel.
Yerel Uygulama Daha İyi Olduğunda
- Yüksek performanslı oyun — 60+ FPS, karmaşık 3D oluşturma.
- Derin donanım entegrasyonu — Bluetooth çevre birimleri, NFC, ARKit/ARCore.
- Arka planda işleme — Müzik akışı, fitness takibi.
- Yüksek kullanıcı etkileşimi gerekiyor — Sosyal medya, mesajlaşma.
Performans Karşılaştırmaları
| Metrik | PWA'dan önce | PWA'dan sonra | İyileştirme |
|---|---|---|---|
| İlk yükleme | 5'ler | 1,2 saniye | %76 daha hızlı |
| Sonraki yükler | 3'ler | 0,3s | %90 daha hızlı |
| Hemen çıkma oranı | 45% | 20% | %55 azalma |
| Dönüşüm oranı | 2% | 3.5% | %75 artış |
| Oturum başına sayfa sayısı | 3 | 5 | %66 artış |
Gerçek dünya sonuçları:
| Şirket | Anahtar Metrik | İyileştirme |
|---|---|---|
| Twitter (X) Lite | Oturum başına gönderilen Tweetler | %75 artış |
| Pinterest'te | Harcanan zaman | %40 artış |
| Über | Rezervasyonun tamamlanması | 2G'de bile sorunsuz |
| Starbucks | Günlük aktif kullanıcılar | 2 kat artış |
| Alibaba'nın | Dönüşüm oranı | %76 artış |
PWA Testi ve Denetimi
Lighthouse PWA Denetimi
# Run Lighthouse from CLI
npx lighthouse https://example.com --view --preset=desktop
# PWA-specific checks:
# - ✅ Registers a service worker
# - ✅ Responds with 200 when offline
# - ✅ Web app manifest exists
# - ✅ Manifest has display: standalone
# - ✅ Served over HTTPS
# - ✅ Redirects HTTP to HTTPS
# - ✅ Configurable start URL
# - ✅ Icons provided (192px and 512px)
Manuel Test Kontrol Listesi
## PWA Pre-Flight Checklist
- [ ] HTTPS enabled
- [ ] Service worker registered and active
- [ ] Pages load offline (test with airplane mode)
- [ ] Manifest configured with correct icons
- [ ] "Add to Home Screen" prompt appears
- [ ] PWA opens in standalone mode with no address bar
- [ ] Splash screen displays correctly
- [ ] Push notifications work
- [ ] All pages are responsive (mobile, tablet, desktop)
- [ ] Performance budget met (Lighthouse score > 90)
- [ ] Cross-browser tested (Chrome, Firefox, Safari, Edge)
- [ ] Accessibility audit passes
Gelişmiş PWA Özellikleri
Arka Plan Senkronizasyonu
// Register a sync event
async function scheduleSync() {
const registration = await navigator.serviceWorker.ready;
await registration.sync.register('sync-pending-data');
}
// Service worker handles sync
self.addEventListener('sync', event => {
if (event.tag === 'sync-pending-data') {
event.waitUntil(syncPendingData());
}
});
Periyodik Arka Plan Senkronizasyonu
// Request periodic sync (requires user engagement)
const registration = await navigator.serviceWorker.ready;
await registration.periodicSync.register('update-content', {
minInterval: 24 * 60 * 60 * 1000 // Once per day
});
Web Paylaşımı API'si
// Share content using native share sheet
async function shareContent(title, text, url) {
if (navigator.share) {
await navigator.share({ title, text, url });
} else {
// Fallback to custom share UI
copyToClipboard(url);
}
}
Kimlik Kartı API'si
// Set app icon badge
if (navigator.setAppBadge) {
await navigator.setAppBadge(5); // Show "5" on icon
} else if (navigator.clearAppBadge) {
await navigator.clearAppBadge();
}
Yaygın Tuzaklar
| Tuzak | Sorun | Çözüm |
|---|---|---|
| Aşırı önbelleğe alma | Uygulama eski içerik sunuyor | Sürüm önbellekleri, kabuk için önbelleği öncelikli, içerik için öncelikli ağı uygulayın |
| Çevrimdışı geri dönüş yok | Beyaz ekran çevrimdışı | /offline.html'yi her zaman anlamlı bir mesajla önbelleğe alın |
| Önbellek bozma hataları | Eski servis çalışanı ısrar ediyor | skipWaiting() ve client.claim()'i kullanın |
| Açılış ekranı sorunları | Yanlış tema rengi veya simgesi | Bildiriyi doğru şekilde yapılandırın, maskelenebilir simgeler kullanın |
| Safari uyumluluğu | Bazı özellikler sınırlıdır (itme, periyodik senkronizasyon) | Aşamalı iyileştirme — zarif bir şekilde bozulma |
| Büyük önbellek boyutu | Depolama kotası aşıldı (kaynak başına 50 MB) | Eski önbellekleri budayın, önbellek boyutunu sınırlayın |
| Başlangıç_url'si eksik | Ana ekran yanlış sayfayı açıyor | Bildirimde her zaman start_url'yi belirtin |
Sonuç
Progresif Web Uygulamaları her iki dünyanın da en iyi yönlerini temsil eder; web'in erişimi ve erişilebilirliği ile yerel uygulamaların yetenekleri ve kullanıcı deneyimi bir araya gelir.
PWA Ne Zaman Oluşturulmalı?
- Maksimum erişim istiyorsunuz — Tüm platformlar için tek uygulama.
- Kullanıcının çevrimdışı erişime ihtiyacı var — Hizmet çalışanının önbelleğe alınması.
- Anında yükleme istiyorsunuz — Uygulama kabuğu mimarisi.
- Yeniden etkileşime geçmek istiyorsunuz — Uygulama mağazası olmadan anlık bildirimler.
- İçerik odaklı bir uygulamanız var - Haberler, e-ticaret, yardımcı programlar.
Başlarken
- Bir manifest.json ve simgeler ekleyin.
- Çevrimdışı geri dönüşe sahip bir hizmet çalışanı oluşturun.
- Lighthouse denetimi çalıştırın ve kritik sorunları düzeltin.
- Gerçek cihazlarda (Android, iOS, masaüstü) test edin.
- Kullanıcı etkileşimini dağıtın ve ölçün.
PWA'lar her senaryoda yerel uygulamaların yerini almaz; ancak içerik odaklı ve yardımcı program uygulamalarının çoğu için, geliştirme maliyetinin çok küçük bir kısmıyla üstün bir kullanıcı deneyimi sağlarlar.
Öne Çıkan Makaleler

2026’da Oluşturmaya Değer 50 Claude Skill
Claude Skills, tekrarlanan promptları yeniden kullanılabilir workflow’lara dönüştürebilir. İşte yazma, kodlama, araştırma, iş, üretkenlik, tasarım, analiz ve destek için 50 pratik Claude Skill.

Senior Engineer’lardan Öğrendiğim 6 Kodlama Pattern’i

Özel Bir Yapay Zeka Not Asistanı Kurun
Markdown notlarını özel taslakları açığa çıkarmadan aranabilir bir asistana dönüştürün.
Yorumlar
0 yorumHenüz onaylı yorum yok. Yeni yanıtlar moderasyon bekleyebilir.