Service Worker Implementierung
Diese Anleitung zeigt die Implementierung eines Service Workers mit Workbox für moderne SPA-Frameworks.
Voraussetzungen
- Node.js 18+
- HTTPS-Umgebung (oder localhost)
- Build-Tool (Vite, Webpack, etc.)
- Grundkenntnisse JavaScript/TypeScript
Schritt 1: Workbox Installation
# npm
npm install workbox-core workbox-routing workbox-strategies
npm install workbox-precaching workbox-expiration
npm install -D vite-plugin-pwa # Für Vite
# yarn
yarn add workbox-core workbox-routing workbox-strategies
yarn add workbox-precaching workbox-expiration
yarn add -D vite-plugin-pwa
Schritt 2: Service Worker erstellen
Basis-Setup (sw.ts)
// src/sw.ts
import { precacheAndRoute, cleanupOutdatedCaches } from 'workbox-precaching';
import { registerRoute } from 'workbox-routing';
import { CacheFirst, StaleWhileRevalidate, NetworkFirst } from 'workbox-strategies';
import { ExpirationPlugin } from 'workbox-expiration';
import { CacheableResponsePlugin } from 'workbox-cacheable-response';
declare let self: ServiceWorkerGlobalScope;
// Versionierung
const CACHE_VERSION = 'v1';
// Alte Caches bereinigen
cleanupOutdatedCaches();
// Precaching (von Build-Tool generiert)
precacheAndRoute(self.__WB_MANIFEST);
// Statische Assets: Cache First
registerRoute(
({ request }) =>
request.destination === 'script' ||
request.destination === 'style',
new CacheFirst({
cacheName: `static-${CACHE_VERSION}`,
plugins: [
new ExpirationPlugin({
maxEntries: 50,
maxAgeSeconds: 60 * 60 * 24 * 30, // 30 Tage
}),
],
})
);
// Fonts: Cache First mit langer TTL
registerRoute(
({ request }) => request.destination === 'font',
new CacheFirst({
cacheName: `fonts-${CACHE_VERSION}`,
plugins: [
new ExpirationPlugin({
maxEntries: 20,
maxAgeSeconds: 60 * 60 * 24 * 365, // 1 Jahr
}),
],
})
);
// Bilder: Cache First
registerRoute(
({ request }) => request.destination === 'image',
new CacheFirst({
cacheName: `images-${CACHE_VERSION}`,
plugins: [
new ExpirationPlugin({
maxEntries: 100,
maxAgeSeconds: 60 * 60 * 24 * 30, // 30 Tage
}),
],
})
);
// API: Stale-While-Revalidate
registerRoute(
({ url }) => url.pathname.startsWith('/api/'),
new StaleWhileRevalidate({
cacheName: `api-${CACHE_VERSION}`,
plugins: [
new CacheableResponsePlugin({
statuses: [200],
}),
new ExpirationPlugin({
maxEntries: 100,
maxAgeSeconds: 60 * 60, // 1 Stunde
}),
],
})
);
Schritt 3: Framework-Integration
Vue 3 + Vite
// vite.config.ts
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import { VitePWA } from 'vite-plugin-pwa';
export default defineConfig({
plugins: [
vue(),
VitePWA({
registerType: 'autoUpdate',
workbox: {
globPatterns: ['**/*.{js,css,html,ico,png,svg,woff2}'],
runtimeCaching: [
{
urlPattern: /^https:\/\/api\.example\.com\/.*$/,
handler: 'StaleWhileRevalidate',
options: {
cacheName: 'api-cache',
expiration: {
maxEntries: 100,
maxAgeSeconds: 60 * 60,
},
},
},
],
},
manifest: {
name: 'Meine App',
short_name: 'App',
theme_color: '#ffffff',
icons: [
{
src: '/icon-192.png',
sizes: '192x192',
type: 'image/png',
},
{
src: '/icon-512.png',
sizes: '512x512',
type: 'image/png',
},
],
},
}),
],
});
React + Vite
// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { VitePWA } from 'vite-plugin-pwa';
export default defineConfig({
plugins: [
react(),
VitePWA({
registerType: 'autoUpdate',
strategies: 'injectManifest',
srcDir: 'src',
filename: 'sw.ts',
injectManifest: {
globPatterns: ['**/*.{js,css,html,ico,png,svg}'],
},
}),
],
});
Angular
# Angular PWA Schematic
ng add @angular/pwa
// ngsw-config.json
{
"index": "/index.html",
"assetGroups": [
{
"name": "app",
"installMode": "prefetch",
"resources": {
"files": [
"/favicon.ico",
"/index.html",
"/*.css",
"/*.js"
]
}
},
{
"name": "assets",
"installMode": "lazy",
"updateMode": "prefetch",
"resources": {
"files": [
"/assets/**"
]
}
}
],
"dataGroups": [
{
"name": "api",
"urls": ["/api/**"],
"cacheConfig": {
"strategy": "freshness",
"maxSize": 100,
"maxAge": "1h"
}
}
]
}
Schritt 4: Registrierung
Manuelle Registrierung
// src/registerSW.ts
export async function registerServiceWorker() {
if (!('serviceWorker' in navigator)) {
console.warn('Service Worker nicht unterstützt');
return;
}
try {
const registration = await navigator.serviceWorker.register('/sw.js', {
scope: '/',
});
// Update-Handling
registration.addEventListener('updatefound', () => {
const newWorker = registration.installing;
newWorker?.addEventListener('statechange', () => {
if (newWorker.state === 'installed' && navigator.serviceWorker.controller) {
// Neuer SW verfügbar
showUpdateNotification();
}
});
});
console.log('Service Worker registriert:', registration.scope);
} catch (error) {
console.error('SW Registrierung fehlgeschlagen:', error);
}
}
Mit vite-plugin-pwa
// src/main.ts
import { registerSW } from 'virtual:pwa-register';
const updateSW = registerSW({
onNeedRefresh() {
// Benutzer fragen, ob aktualisiert werden soll
if (confirm('Neue Version verfügbar. Jetzt aktualisieren?')) {
updateSW(true);
}
},
onOfflineReady() {
console.log('App ist offline-bereit');
},
});
Schritt 5: Update-Strategie
skipWaiting + clients.claim
// sw.ts
self.addEventListener('message', (event) => {
if (event.data && event.data.type === 'SKIP_WAITING') {
self.skipWaiting();
}
});
self.addEventListener('activate', (event) => {
event.waitUntil(self.clients.claim());
});
Prompt-basiertes Update
// In der App
function promptUpdate(registration: ServiceWorkerRegistration) {
const toast = document.createElement('div');
toast.innerHTML = `
<p>Neue Version verfügbar</p>
<button id="update">Aktualisieren</button>
<button id="dismiss">Später</button>
`;
document.getElementById('update')?.addEventListener('click', () => {
registration.waiting?.postMessage({ type: 'SKIP_WAITING' });
window.location.reload();
});
}
Wichtige Befehle
# Build mit SW-Generierung
npm run build
# Entwicklung (SW deaktiviert)
npm run dev
# Produktion lokal testen
npm run preview
# SW im Browser deregistrieren (DevTools)
# Application > Service Workers > Unregister
Debugging
Chrome DevTools
- Application Tab > Service Workers
- Aktive SW, Waiting SW, Update-Status sichtbar
- "Update on reload" für Entwicklung aktivieren
Nützliche Logs
// sw.ts
self.addEventListener('install', (event) => {
console.log('[SW] Installing version:', CACHE_VERSION);
});
self.addEventListener('activate', (event) => {
console.log('[SW] Activating version:', CACHE_VERSION);
});
self.addEventListener('fetch', (event) => {
console.log('[SW] Fetching:', event.request.url);
});
Nächste Schritte
- Push-Benachrichtigungen implementieren
- Background Sync einrichten
- Offline-Fallback-Seite erstellen
- Cache-Analytics integrieren
- Performance-Monitoring
Verwandte Themen
- Workbox-Dokumentation
- PWA-Manifest
- Cache API
- vite-plugin-pwa
- @angular/pwa
CFTools Software unterstützt bei der Service Worker Implementierung für Enterprise-Anwendungen.