Skip to content

Çevrimdışı web oyunları için PWA ​

Progresif Web Uygulamaları, oyuncuların oyununuzu yüklemesine ve çevrimdışı oynamasına olanak tanır. Bu eğitim, bir web oyununa PWA desteğinin nasıl ekleneceğini gösterir.

1) Web Uygulaması Manifesti ​

manifest.json dosyasını oluşturun:

json
{
  "name": "Harika Oyunum",
  "short_name": "HarikaOyun",
  "description": "Çevrimdışı oynayabileceğiniz harika bir oyun",
  "start_url": "/",
  "display": "fullscreen",
  "orientation": "landscape",
  "background_color": "#1a1a2e",
  "theme_color": "#4ade80",
  "icons": [
    {
      "src": "/icons/icon-192.png",
      "sizes": "192x192",
      "type": "image/png"
    },
    {
      "src": "/icons/icon-512.png",
      "sizes": "512x512",
      "type": "image/png"
    },
    {
      "src": "/icons/icon-maskable.png",
      "sizes": "512x512",
      "type": "image/png",
      "purpose": "maskable"
    }
  ]
}

HTML dosyanızda buna bağlantı verin:

html
<link rel="manifest" href="/manifest.json">
<meta name="theme-color" content="#4ade80">
<link rel="apple-touch-icon" href="/icons/icon-192.png">

2) Temel service worker ​

sw.js dosyasını oluşturun:

js
const CACHE_NAME = 'game-v1'

const ASSETS = [
  '/',
  '/index.html',
  '/game.js',
  '/style.css',
  '/assets/sprites.png',
  '/assets/sounds/jump.mp3',
  '/assets/sounds/music.mp3',
]

// Install: cache assets
self.addEventListener('install', (event) => {
  event.waitUntil(
    caches.open(CACHE_NAME).then((cache) => {
      return cache.addAll(ASSETS)
    })
  )
})

// Activate: clean old caches
self.addEventListener('activate', (event) => {
  event.waitUntil(
    caches.keys().then((keys) => {
      return Promise.all(
        keys.filter(key => key !== CACHE_NAME)
            .map(key => caches.delete(key))
      )
    })
  )
})

// Fetch: serve from cache, fall back to network
self.addEventListener('fetch', (event) => {
  event.respondWith(
    caches.match(event.request).then((cached) => {
      return cached || fetch(event.request)
    })
  )
})

3) Service worker'ı kaydetme ​

js
if ('serviceWorker' in navigator) {
  window.addEventListener('load', async () => {
    try {
      const registration = await navigator.serviceWorker.register('/sw.js')
      console.log('SW registered:', registration.scope)
    } catch (err) {
      console.error('SW registration failed:', err)
    }
  })
}

4) Ağ üzerinden güncellemeli önbellek öncelikli strateji ​

Oyunlar için daha iyidir: arka planda güncellemeler yapılırken hızlı yükleme sağlar.

js
self.addEventListener('fetch', (event) => {
  event.respondWith(
    caches.open(CACHE_NAME).then(async (cache) => {
      const cached = await cache.match(event.request)
      
      // Start network fetch in background
      const fetchPromise = fetch(event.request).then((response) => {
        if (response.ok) {
          cache.put(event.request, response.clone())
        }
        return response
      }).catch(() => null)
      
      // Return cached immediately, or wait for network
      return cached || fetchPromise
    })
  )
})

5) Güncellemeler için sürümlendirilmiş önbellek ​

js
const CACHE_VERSION = 'v2'
const STATIC_CACHE = `static-${CACHE_VERSION}`
const DYNAMIC_CACHE = `dynamic-${CACHE_VERSION}`

const STATIC_ASSETS = [
  '/',
  '/index.html',
  '/game.js',
  // ... core assets that rarely change
]

self.addEventListener('install', (event) => {
  self.skipWaiting() // Activate immediately
  
  event.waitUntil(
    caches.open(STATIC_CACHE).then(cache => cache.addAll(STATIC_ASSETS))
  )
})

self.addEventListener('activate', (event) => {
  event.waitUntil(
    caches.keys().then(keys => {
      return Promise.all(
        keys.filter(key => !key.includes(CACHE_VERSION))
            .map(key => caches.delete(key))
      )
    })
  )
  
  clients.claim() // Take control immediately
})

6) Oyun güncellemelerini yönetme ​

Bir güncelleme kullanıma sunulduğunda oyuncuları bilgilendirin:

js
// In main app
let refreshing = false

navigator.serviceWorker.addEventListener('controllerchange', () => {
  if (!refreshing) {
    refreshing = true
    showUpdateNotification()
  }
})

function showUpdateNotification() {
  const banner = document.createElement('div')
  banner.innerHTML = `
    <p>Oyun güncellendi! En son sürümü edinmek için yenileyin.</p>
    <button onclick="location.reload()">Yenile</button>
  `
  banner.className = 'update-banner'
  document.body.appendChild(banner)
}

7) Çevrimdışı durumu algılama ​

js
function updateOnlineStatus() {
  if (navigator.onLine) {
    hideOfflineBanner()
    syncGameData()
  } else {
    showOfflineBanner()
  }
}

window.addEventListener('online', updateOnlineStatus)
window.addEventListener('offline', updateOnlineStatus)

function showOfflineBanner() {
  document.getElementById('offline-banner').style.display = 'block'
}

function hideOfflineBanner() {
  document.getElementById('offline-banner').style.display = 'none'
}

8) Yükleme istemi ​

js
let deferredPrompt = null

window.addEventListener('beforeinstallprompt', (e) => {
  e.preventDefault()
  deferredPrompt = e
  showInstallButton()
})

function showInstallButton() {
  const btn = document.getElementById('install-btn')
  btn.style.display = 'block'
  btn.addEventListener('click', installApp)
}

async function installApp() {
  if (!deferredPrompt) return
  
  deferredPrompt.prompt()
  const { outcome } = await deferredPrompt.userChoice
  
  console.log('Install prompt outcome:', outcome)
  deferredPrompt = null
  document.getElementById('install-btn').style.display = 'none'
}

window.addEventListener('appinstalled', () => {
  console.log('App installed!')
  deferredPrompt = null
})

iOS ve iPadOS'te beforeinstallprompt olayı hiçbir zaman tetiklenmez; bu nedenle yukarıdaki yükleme düğmesi yalnızca Chromium tarayıcılarında görünür. iPhone ve iPad kullanıcıları, Paylaş'a ve ardından "Ana Ekrana Ekle" seçeneğine dokunarak yükleme yapar. 2026'da değişen ve bilinmesi gereken bir nokta var: Safari 26'dan (iOS 26 / iPadOS 26) itibaren Ana Ekran'a eklenen her site, varsayılan olarak etkin olan "Web Uygulaması Olarak Aç" seçeneğiyle birlikte varsayılan olarak bir web uygulaması şeklinde açılır. iOS'te artık yüklenebilirlik şartı yoktur; ancak manifest ve service worker sunmak yine de çok daha iyi bir çevrimdışı deneyim sağlar.

9) Liderlik tabloları için arka plan senkronizasyonu ​

Destek konusunda önemli bir not: Background Sync API yalnızca Chromium tarayıcılarında (Chrome, Edge, Opera, Samsung Internet) uygulanmıştır. Firefox ve Safari (iOS dâhil) bunu desteklemez; bu nedenle her zaman özellik algılama kullanın ve skorları uygulamanın bir sonraki normal yüklenişinde gönderen bir yedek yöntem bulundurun.

js
// In service worker
self.addEventListener('sync', (event) => {
  if (event.tag === 'sync-scores') {
    event.waitUntil(syncScores())
  }
})

async function syncScores() {
  const db = await openDB('game', 1)
  const pendingScores = await db.getAll('pending-scores')
  
  for (const score of pendingScores) {
    try {
      await fetch('/api/scores', {
        method: 'POST',
        body: JSON.stringify(score),
        headers: { 'Content-Type': 'application/json' }
      })
      await db.delete('pending-scores', score.id)
    } catch (err) {
      // Will retry on next sync
      break
    }
  }
}

// In main app
async function submitScore(score) {
  try {
    await fetch('/api/scores', { method: 'POST', body: JSON.stringify(score) })
  } catch {
    // Save for later sync
    const db = await openDB('game', 1)
    await db.add('pending-scores', { ...score, id: Date.now() })
    
    if ('serviceWorker' in navigator && 'sync' in window.registration) {
      await navigator.serviceWorker.ready
      await registration.sync.register('sync-scores')
    }
  }
}

10) PWA'yı test etme ​

Chrome DevTools:

  • Application > Service Workers
  • Application > Manifest
  • Application > Cache Storage
  • Network > Offline onay kutusu

Lighthouse denetimi:

  • PWA denetimini çalıştırın
  • Yüklenebilirliği kontrol edin
  • Çevrimdışı çalışma özelliğini kontrol edin

Gerçek cihazda test:

  • Telefonun ana ekranına yükleyin
  • Uçak modunu etkinleştirin
  • Tüm özellikleri çevrimdışı test edin

İlgili içerikler ​

Harici Kaynaklar ​