web-design

Progressive Web Apps (PWA) 2025: Native Deneyimli Web Uygulamaları

Progressive Web Apps (PWA) 2025 Progressive Web Apps, web teknolojileri ile native uygulama deneyimi sunan modern çözüm. Offline çalışma, push notifications ve kurulabilirlik ile PWA'ler, mobil ...

Progressive Web Apps (PWA) 2025

Progressive Web Apps, web teknolojileri ile native uygulama deneyimi sunan modern çözüm. Offline çalışma, push notifications ve kurulabilirlik ile PWA'ler, mobil uygulamaların yerini alıyor!

PWA Nedir?

Progressive Web App özellikleri:

  • 📱 İnstallable - Ana ekrana eklenebilir
  • 🔌 Offline - İnternetsiz çalışır
  • 🚀 Fast - Hızlı yüklenir
  • 🔔 Engaging - Push notifications
  • 📲 Native-like - Uygulama gibi hissettirir
  • 🌐 Progressive - Her tarayıcıda çalışır

PWA vs Native App

| Özellik | PWA | Native App | |---------|-----|------------| | Geliştirme | Web teknolojileri | Platform specific | | Dağıtım | URL | App Store | | Güncelleme | Instant | Store approval | | Boyut | ~1-5 MB | 50-200 MB | | Offline | ✅ Service Worker | ✅ Built-in | | Push Notif | ✅ | ✅ | | Maliyet | Düşük | Yüksek | | SEO | ✅ İndekslenebilir | ❌ |

Core Components

1. Web App Manifest

{
  "name": "My PWA App",
  "short_name": "MyPWA",
  "description": "Amazing Progressive Web App",
  "start_url": "/",
  "display": "standalone",
  "background_color": "#ffffff",
  "theme_color": "#0066cc",
  "orientation": "portrait-primary",
  "scope": "/",
  
  "icons": [
    {
      "src": "/icons/icon-72x72.png",
      "sizes": "72x72",
      "type": "image/png",
      "purpose": "maskable any"
    },
    {
      "src": "/icons/icon-96x96.png",
      "sizes": "96x96",
      "type": "image/png"
    },
    {
      "src": "/icons/icon-128x128.png",
      "sizes": "128x128",
      "type": "image/png"
    },
    {
      "src": "/icons/icon-144x144.png",
      "sizes": "144x144",
      "type": "image/png"
    },
    {
      "src": "/icons/icon-152x152.png",
      "sizes": "152x152",
      "type": "image/png"
    },
    {
      "src": "/icons/icon-192x192.png",
      "sizes": "192x192",
      "type": "image/png"
    },
    {
      "src": "/icons/icon-384x384.png",
      "sizes": "384x384",
      "type": "image/png"
    },
    {
      "src": "/icons/icon-512x512.png",
      "sizes": "512x512",
      "type": "image/png"
    }
  ],
  
  "screenshots": [
    {
      "src": "/screenshots/home.png",
      "sizes": "540x720",
      "type": "image/png"
    }
  ],
  
  "categories": ["business", "productivity"],
  "shortcuts": [
    {
      "name": "New Task",
      "url": "/new-task",
      "icons": [{ "src": "/icons/new.png", "sizes": "96x96" }]
    }
  ]
}
<!-- HTML'de manifest -->
<link rel="manifest" href="/manifest.json">
<meta name="theme-color" content="#0066cc">
<link rel="apple-touch-icon" href="/icons/icon-192x192.png">

2. Service Worker

service-worker.js:

const CACHE_NAME = 'my-pwa-v1';
const urlsToCache = [
  '/',
  '/index.html',
  '/styles/main.css',
  '/scripts/app.js',
  '/images/logo.png'
];

// Install Event
self.addEventListener('install', (event) => {
  event.waitUntil(
    caches.open(CACHE_NAME)
      .then((cache) => {
        console.log('Opened cache');
        return cache.addAll(urlsToCache);
      })
  );
  // Force new service worker to activate immediately
  self.skipWaiting();
});

// Activate Event
self.addEventListener('activate', (event) => {
  event.waitUntil(
    caches.keys().then((cacheNames) => {
      return Promise.all(
        cacheNames.map((cacheName) => {
          if (cacheName !== CACHE_NAME) {
            console.log('Deleting old cache:', cacheName);
            return caches.delete(cacheName);
          }
        })
      );
    })
  );
  // Take control immediately
  return self.clients.claim();
});

// Fetch Event - Caching Strategy
self.addEventListener('fetch', (event) => {
  event.respondWith(
    caches.match(event.request)
      .then((response) => {
        // Cache hit - return response
        if (response) {
          return response;
        }
        
        // Clone request
        const fetchRequest = event.request.clone();
        
        return fetch(fetchRequest).then((response) => {
          // Check if valid response
          if (!response || response.status !== 200 || response.type !== 'basic') {
            return response;
          }
          
          // Clone response
          const responseToCache = response.clone();
          
          caches.open(CACHE_NAME)
            .then((cache) => {
              cache.put(event.request, responseToCache);
            });
          
          return response;
        });
      })
  );
});

Kayıt (main.js):

// Service Worker Registration
if ('serviceWorker' in navigator) {
  window.addEventListener('load', () => {
    navigator.serviceWorker.register('/service-worker.js')
      .then((registration) => {
        console.log('SW registered:', registration.scope);
        
        // Check for updates
        registration.update();
        
        // Listen for updates
        registration.addEventListener('updatefound', () => {
          const newWorker = registration.installing;
          
          newWorker.addEventListener('statechange', () => {
            if (newWorker.state === 'installed' && navigator.serviceWorker.controller) {
              // New version available
              showUpdateNotification();
            }
          });
        });
      })
      .catch((error) => {
        console.log('SW registration failed:', error);
      });
  });
}

function showUpdateNotification() {
  if (confirm('New version available! Reload to update?')) {
    window.location.reload();
  }
}

Caching Strategies

1. Cache First (Offline First)

// Best for: Static assets
self.addEventListener('fetch', (event) => {
  event.respondWith(
    caches.match(event.request)
      .then((cached) => cached || fetch(event.request))
  );
});

2. Network First

// Best for: API calls, dynamic content
self.addEventListener('fetch', (event) => {
  event.respondWith(
    fetch(event.request)
      .then((response) => {
        const clone = response.clone();
        caches.open(CACHE_NAME).then((cache) => {
          cache.put(event.request, clone);
        });
        return response;
      })
      .catch(() => caches.match(event.request))
  );
});

3. Stale While Revalidate

// Best for: Frequently updated content
self.addEventListener('fetch', (event) => {
  event.respondWith(
    caches.match(event.request).then((cached) => {
      const fetched = fetch(event.request).then((response) => {
        const clone = response.clone();
        caches.open(CACHE_NAME).then((cache) => {
          cache.put(event.request, clone);
        });
        return response;
      });
      
      return cached || fetched;
    })
  );
});

4. Network Only

// Best for: Real-time data, analytics
self.addEventListener('fetch', (event) => {
  if (event.request.url.includes('/api/realtime')) {
    event.respondWith(fetch(event.request));
  }
});

Push Notifications

1. Request Permission

async function requestNotificationPermission() {
  const permission = await Notification.requestPermission();
  
  if (permission === 'granted') {
    console.log('Notification permission granted');
    await subscribeUser();
  }
}

async function subscribeUser() {
  const registration = await navigator.serviceWorker.ready;
  
  const subscription = await registration.pushManager.subscribe({
    userVisibleOnly: true,
    applicationServerKey: urlBase64ToUint8Array(PUBLIC_VAPID_KEY)
  });
  
  // Send subscription to server
  await fetch('/api/subscribe', {
    method: 'POST',
    body: JSON.stringify(subscription),
    headers: {
      'Content-Type': 'application/json'
    }
  });
}

2. Handle Push Event (Service Worker)

self.addEventListener('push', (event) => {
  const data = event.data.json();
  
  const options = {
    body: data.body,
    icon: '/icons/icon-192x192.png',
    badge: '/icons/badge-72x72.png',
    vibrate: [200, 100, 200],
    data: {
      url: data.url
    },
    actions: [
      {
        action: 'open',
        title: 'Open'
      },
      {
        action: 'close',
        title: 'Close'
      }
    ]
  };
  
  event.waitUntil(
    self.registration.showNotification(data.title, options)
  );
});

self.addEventListener('notificationclick', (event) => {
  event.notification.close();
  
  if (event.action === 'open') {
    event.waitUntil(
      clients.openWindow(event.notification.data.url)
    );
  }
});

3. Server-Side (Node.js)

const webpush = require('web-push');

// Configure VAPID keys
webpush.setVapidDetails(
  'mailto:your@email.com',
  PUBLIC_VAPID_KEY,
  PRIVATE_VAPID_KEY
);

// Send notification
async function sendNotification(subscription, payload) {
  try {
    await webpush.sendNotification(subscription, JSON.stringify(payload));
    console.log('Notification sent');
  } catch (error) {
    console.error('Error sending notification:', error);
  }
}

// Usage
const payload = {
  title: 'New Message!',
  body: 'You have a new message',
  url: '/messages'
};

sendNotification(userSubscription, payload);

Offline Functionality

1. Offline Page

// service-worker.js
const OFFLINE_PAGE = '/offline.html';

self.addEventListener('install', (event) => {
  event.waitUntil(
    caches.open(CACHE_NAME)
      .then((cache) => cache.add(OFFLINE_PAGE))
  );
});

self.addEventListener('fetch', (event) => {
  if (event.request.mode === 'navigate') {
    event.respondWith(
      fetch(event.request)
        .catch(() => caches.match(OFFLINE_PAGE))
    );
  }
});

2. Background Sync

// Register sync
async function registerBackgroundSync() {
  const registration = await navigator.serviceWorker.ready;
  
  try {
    await registration.sync.register('sync-messages');
    console.log('Sync registered');
  } catch (error) {
    console.log('Sync registration failed:', error);
  }
}

// Service Worker: Handle sync
self.addEventListener('sync', (event) => {
  if (event.tag === 'sync-messages') {
    event.waitUntil(syncMessages());
  }
});

async function syncMessages() {
  const messages = await getQueuedMessages();
  
  for (const message of messages) {
    try {
      await fetch('/api/messages', {
        method: 'POST',
        body: JSON.stringify(message)
      });
      
      await removeFromQueue(message.id);
    } catch (error) {
      console.error('Sync failed:', error);
    }
  }
}

Installation Prompt

let deferredPrompt;

window.addEventListener('beforeinstallprompt', (e) => {
  // Prevent default mini-infobar
  e.preventDefault();
  
  // Save event for later
  deferredPrompt = e;
  
  // Show custom install button
  showInstallButton();
});

function showInstallButton() {
  const installButton = document.getElementById('install-button');
  installButton.style.display = 'block';
  
  installButton.addEventListener('click', async () => {
    // Show install prompt
    deferredPrompt.prompt();
    
    // Wait for user response
    const { outcome } = await deferredPrompt.userChoice;
    console.log(`User response: ${outcome}`);
    
    // Clear deferredPrompt
    deferredPrompt = null;
    
    // Hide install button
    installButton.style.display = 'none';
  });
}

// Listen for app installed
window.addEventListener('appinstalled', () => {
  console.log('PWA installed');
  
  // Track installation
  gtag('event', 'pwa_install');
});

Advanced Features

1. Web Share API

async function shareContent() {
  if (navigator.share) {
    try {
      await navigator.share({
        title: 'Check this out!',
        text: 'Amazing PWA',
        url: window.location.href
      });
      
      console.log('Shared successfully');
    } catch (error) {
      console.log('Share failed:', error);
    }
  }
}

2. File System Access

async function saveFile() {
  try {
    const handle = await window.showSaveFilePicker({
      suggestedName: 'document.txt',
      types: [{
        description: 'Text Files',
        accept: { 'text/plain': ['.txt'] }
      }]
    });
    
    const writable = await handle.createWritable();
    await writable.write(content);
    await writable.close();
    
    console.log('File saved');
  } catch (error) {
    console.log('Save cancelled:', error);
  }
}

3. Badge API

// Set badge count
navigator.setAppBadge(5);

// Clear badge
navigator.clearAppBadge();

Performance Best Practices

1. Precaching

// Workbox example
import { precacheAndRoute } from 'workbox-precaching';

precacheAndRoute(self.__WB_MANIFEST);

2. Route Caching

import { registerRoute } from 'workbox-routing';
import { CacheFirst, NetworkFirst } from 'workbox-strategies';

// Images: Cache First
registerRoute(
  ({ request }) => request.destination === 'image',
  new CacheFirst({
    cacheName: 'images',
    plugins: [
      new ExpirationPlugin({
        maxEntries: 60,
        maxAgeSeconds: 30 * 24 * 60 * 60 // 30 days
      })
    ]
  })
);

// API: Network First
registerRoute(
  ({ url }) => url.pathname.startsWith('/api/'),
  new NetworkFirst({
    cacheName: 'api',
    networkTimeoutSeconds: 3
  })
);

Testing & Debugging

Chrome DevTools

// Application tab
// - Manifest
// - Service Workers
// - Cache Storage
// - Local Storage

// Lighthouse PWA Audit
// - Installable
// - PWA Optimized
// - Offline capable

PWA Checklist

Manifest:

  • [ ] manifest.json configured
  • [ ] Icons (192x192, 512x512)
  • [ ] start_url defined
  • [ ] display: standalone
  • [ ] theme_color set

Service Worker:

  • [ ] Registered correctly
  • [ ] Caching strategy
  • [ ] Offline fallback
  • [ ] Updates handled

HTTPS:

  • [ ] Served over HTTPS
  • [ ] Valid SSL certificate

Performance:

  • [ ] Fast loading (< 3s)
  • [ ] Responsive design
  • [ ] Core Web Vitals

Features:

  • [ ] Push notifications
  • [ ] Background sync
  • [ ] Install prompt

Sonuç

PWA'ler 2025'te web'in geleceği:

  • 🚀 Native performans
  • 💰 Düşük maliyet
  • 📱 Her platformda çalışır
  • 🔌 Offline support
  • 🔔 Engagement artışı

PWA Başarı İstatistikleri:

  • Twitter: %65 sayfa görüntülme artışı
  • Starbucks: %2x günlük aktif kullanıcı
  • Pinterest: %60 engagement artışı
  • Forbes: %43 daha fazla oturum

Modern PWA geliştirme hizmeti için bize ulaşın! 📱

📧 iletisim@cesayazilim.com
📞 +90 850 225 53 34

Paylaş

Yazar

Cesa Yazılım

Blog Güncellemeleri

Yeni içeriklerden haberdar olmak için abone olun

Abone Ol

Projenizi Başlatın

Blockchain ve Web3 projeleriniz için ücretsiz danışmanlık alın

İletişime Geçin

WhatsApp'tan Yazın!

Hızlı yanıt için

1

Cesa Yazılım

Çevrimiçi

Size nasıl yardımcı olabiliriz? 💬