Service Worker, Manifest und Caching ohne App-Store-Umweg
Eine PWA mit Vue und Vite verwandelt eine bestehende Webanwendung in eine installierbare, offline-fähige App, ohne App-Store-Review und ohne separate native Codebasis. Mit vite-plugin-pwa lassen sich Service Worker, Web-App-Manifest und Caching-Strategien weitgehend automatisiert konfigurieren, während Update-Handling und Offline-Verhalten gezielt auf die eigene Anwendung zugeschnitten werden.
Inhaltsverzeichnis
- 1. Was eine PWA von einer normalen Vue-Anwendung unterscheidet
- 2. Projekt-Setup mit vite-plugin-pwa
- 3. Das Web-App-Manifest: Installierbarkeit steuern
- 4. Caching-Strategien: Cache First, Network First, Stale While Revalidate
- 5. Offline-Verhalten gezielt gestalten statt dem Zufall überlassen
- 6. Update-Handling: neue Versionen ohne Nutzerverwirrung ausliefern
- 7. API-Daten offline verfügbar machen mit IndexedDB
- 8. Installation-Prompts und Nutzerführung
- 9. PWA im Vergleich zu Capacitor für native Distribution
- 10. Zusammenfassung
- 11. FAQ
1. Was eine PWA von einer normalen Vue-Anwendung unterscheidet
Eine PWA mit Vue unterscheidet sich von einer normalen Webanwendung durch drei technische Zusätze: einen Service Worker, der Netzwerk-Requests abfangen und cachen kann, ein Web-App-Manifest, das dem Betriebssystem Metadaten für die Installation liefert, und eine HTTPS-Verbindung, die für Service Worker zwingend vorausgesetzt wird. Zusammen ermöglichen diese drei Bausteine, dass eine Vue-Anwendung auf dem Homescreen installiert werden kann und auch ohne Netzwerkverbindung zumindest teilweise funktioniert.
Der entscheidende Unterschied zu einer klassischen Webanwendung liegt im Service Worker als eigenständigem, vom Hauptthread getrennten JavaScript-Kontext, der zwischen Netzwerk und Anwendung sitzt. Jede Anfrage der Vue-PWA kann vom Service Worker abgefangen werden, bevor sie überhaupt das Netzwerk erreicht, was gezielte Caching-Entscheidungen ermöglicht: Statische Assets aus dem Cache liefern, API-Antworten mit Fallback auf gecachte Daten versehen, oder komplett offline aus dem Cache antworten.
Für Vue-Entwickler ist der pragmatische Einstieg in eine PWA mit Vue heute deutlich einfacher als noch vor einigen Jahren, weil vite-plugin-pwa die Erstellung von Manifest und Service Worker automatisiert und auf der bewährten Workbox-Bibliothek von Google aufbaut. Statt einen Service Worker von Grund auf selbst zu schreiben, konfiguriert man Caching-Strategien deklarativ in der Vite-Konfiguration, und das Plugin generiert daraus den passenden Service-Worker-Code bei jedem Build.
2. Projekt-Setup mit vite-plugin-pwa
Der Einstieg in eine PWA mit Vue beginnt mit der Installation von vite-plugin-pwa in ein bestehendes Vue-3-Projekt mit Vite als Build-Tool. Das Plugin wird in der vite.config.js registriert und übernimmt ab diesem Zeitpunkt automatisch die Generierung von Manifest-Datei und Service Worker bei jedem Produktions-Build, ohne dass diese Dateien manuell gepflegt werden müssen.
Für die Entwicklung ist besonders die Option devOptions.enabled relevant, die den Service Worker auch im lokalen Vite-Dev-Server aktiviert. Ohne diese Option lässt sich Offline-Verhalten während der Entwicklung nur schwer testen, weil Service Worker standardmäßig nur in Produktions-Builds registriert werden.
# Add the PWA plugin to an existing Vue 3 + Vite project
npm install -D vite-plugin-pwa
// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { VitePWA } from 'vite-plugin-pwa'
export default defineConfig({
plugins: [
vue(),
VitePWA({
registerType: 'prompt', // let the user decide when to activate updates
devOptions: {
enabled: true, // test the service worker during `vite dev`
},
manifest: {
name: 'Mironsoft App',
short_name: 'Mironsoft',
theme_color: '#16a34a',
background_color: '#0f172a',
display: 'standalone',
icons: [
{ src: 'icon-192.png', sizes: '192x192', type: 'image/png' },
{ src: 'icon-512.png', sizes: '512x512', type: 'image/png' },
],
},
workbox: {
globPatterns: ['**/*.{js,css,html,svg,png,webp}'],
},
}),
],
})
Nach dem Build erzeugt vite-plugin-pwa automatisch eine manifest.webmanifest-Datei sowie einen Service-Worker-Bundle im dist-Verzeichnis, beide bereits mit den korrekten Verweisen im HTML-Head verlinkt. Für die meisten Standard-Anwendungsfälle einer PWA mit Vue ist diese Grundkonfiguration bereits ausreichend, um Lighthouse-Anforderungen für Installierbarkeit zu erfüllen.
3. Das Web-App-Manifest: Installierbarkeit steuern
Das Web-App-Manifest ist eine JSON-Datei, die dem Betriebssystem mitteilt, wie eine Vue-PWA nach der Installation aussehen und sich verhalten soll. Felder wie name, icons und start_url sind Pflichtfelder für die Installierbarkeit, während display: standalone dafür sorgt, dass die App ohne Browser-Chrome wie Adressleiste und Tabs geöffnet wird, was das native Erscheinungsbild erheblich verbessert.
Ein häufig übersehenes Detail sind Maskable Icons: Android beschneidet App-Icons in unterschiedliche Formen, je nach Hersteller-Skin, rund, quadratisch mit abgerundeten Ecken oder als Tropfen. Ein Icon mit dem Zusatz purpose: maskable im Manifest signalisiert dem Betriebssystem, dass ausreichend Sicherheitsabstand zum Rand vorhanden ist, damit wichtige visuelle Elemente nicht abgeschnitten werden.
{
"name": "Mironsoft App",
"short_name": "Mironsoft",
"description": "Produktkatalog und Kundenverwaltung",
"start_url": "/?source=pwa",
"display": "standalone",
"theme_color": "#16a34a",
"background_color": "#0f172a",
"orientation": "portrait",
"icons": [
{ "src": "icon-192.png", "sizes": "192x192", "type": "image/png" },
{ "src": "icon-512.png", "sizes": "512x512", "type": "image/png" },
{ "src": "icon-maskable-512.png", "sizes": "512x512", "type": "image/png", "purpose": "maskable" }
]
}
Das Feld start_url mit dem Query-Parameter ?source=pwa ist ein nützliches Muster, um in der Analyse später unterscheiden zu können, ob ein Seitenaufruf von der installierten Vue-PWA oder vom regulären Browser stammt. Diese Information ist wertvoll, um zu verstehen, wie stark die installierte App gegenüber der Browser-Version tatsächlich genutzt wird.
4. Caching-Strategien: Cache First, Network First, Stale While Revalidate
Der Kern jeder PWA mit Vue ist die Entscheidung, welche Caching-Strategie für welche Art von Ressource sinnvoll ist. Statische Assets wie CSS, JavaScript und Bilder ändern sich nur bei einem neuen Deployment und eignen sich daher für die Strategie Cache First: Der Service Worker liefert die Ressource direkt aus dem Cache, ohne überhaupt eine Netzwerkanfrage zu starten, was sowohl Ladezeit als auch Datenverbrauch reduziert.
Für API-Antworten, die sich häufiger ändern, ist Network First oder Stale While Revalidate meist passender. Network First versucht zuerst das Netzwerk und fällt nur bei einem Fehler auf den Cache zurück, was aktuelle Daten priorisiert, aber bei langsamer Verbindung Wartezeit verursacht. Stale While Revalidate liefert sofort die gecachte Version und aktualisiert den Cache im Hintergrund für den nächsten Aufruf, was einen guten Kompromiss zwischen Geschwindigkeit und Aktualität darstellt.
// vite.config.js — workbox runtime caching configuration
VitePWA({
workbox: {
runtimeCaching: [
{
// Static assets: served instantly, never re-fetched until a new build
urlPattern: /\.(?:png|jpg|jpeg|svg|webp|woff2)$/,
handler: 'CacheFirst',
options: {
cacheName: 'static-assets',
expiration: { maxEntries: 100, maxAgeSeconds: 60 * 60 * 24 * 30 },
},
},
{
// Product API: prefer fresh data, but survive brief network drops
urlPattern: /^https:\/\/api\.mironsoft\.de\/products/,
handler: 'NetworkFirst',
options: {
cacheName: 'products-api',
networkTimeoutSeconds: 3,
expiration: { maxEntries: 50, maxAgeSeconds: 60 * 60 },
},
},
{
// Dashboard stats: show cached data instantly, refresh silently
urlPattern: /^https:\/\/api\.mironsoft\.de\/dashboard/,
handler: 'StaleWhileRevalidate',
options: { cacheName: 'dashboard-api' },
},
],
},
})
Die Wahl der richtigen Strategie ist keine rein technische Entscheidung, sondern hängt vom fachlichen Kontext ab. Ein Preis in einem Onlineshop sollte niemals mit Cache First ausgeliefert werden, weil veraltete Preise zu Bestellfehlern führen können. Ein statisches Firmen-Logo dagegen ändert sich praktisch nie und profitiert maximal von aggressivem Caching. Diese fachliche Einordnung sollte vor der technischen Konfiguration jeder Vue-PWA stehen.
5. Offline-Verhalten gezielt gestalten statt dem Zufall überlassen
Eine PWA mit Vue ohne durchdachtes Offline-Verhalten zeigt im besten Fall eine generische Browser-Fehlerseite, im schlechtesten Fall eine leere, verwirrende Ansicht. Eine dedizierte Offline-Fallback-Seite, die der Service Worker ausliefert, sobald sowohl Netzwerk als auch Cache keine Antwort liefern können, verbessert die Nutzererfahrung erheblich und erklärt klar, dass gerade keine Verbindung besteht.
Zusätzlich lohnt es sich, im Vue-Router selbst den Online-Status zu berücksichtigen, etwa über das navigator.onLine-Property in Kombination mit den online- und offline-Events des Browsers. Eine globale Statusanzeige, die den Verbindungsstatus reaktiv widerspiegelt, verhindert, dass Nutzer versuchen, Aktionen auszuführen, die zwangsläufig fehlschlagen werden, weil keine Netzwerkverbindung besteht.
// composables/useOnlineStatus.js
import { ref, onMounted, onUnmounted } from 'vue'
export function useOnlineStatus() {
const isOnline = ref(navigator.onLine)
function updateStatus() {
isOnline.value = navigator.onLine
}
onMounted(() => {
window.addEventListener('online', updateStatus)
window.addEventListener('offline', updateStatus)
})
onUnmounted(() => {
window.removeEventListener('online', updateStatus)
window.removeEventListener('offline', updateStatus)
})
return { isOnline }
}
Ein wichtiger Aspekt bei Vue-PWA-Anwendungen mit Formularen: Statt eine fehlgeschlagene Anfrage einfach mit einer Fehlermeldung zu quittieren, kann eine Background-Sync-Strategie den Request zwischenspeichern und automatisch erneut senden, sobald die Verbindung zurückkehrt. Das erfordert zusätzliche Logik über die Workbox Background Sync API, verbessert aber die Nutzererfahrung bei instabilen Verbindungen erheblich.
6. Update-Handling: neue Versionen ohne Nutzerverwirrung ausliefern
Ein oft unterschätztes Problem bei einer PWA mit Vue: Der Service Worker cacht eine Version der Anwendung so effektiv, dass Nutzer nach einem Deployment möglicherweise tagelang die alte Version sehen, weil der neue Service Worker zwar im Hintergrund installiert, aber erst nach einem vollständigen Schließen aller Tabs aktiviert wird. Ohne aktives Update-Handling bleibt diese Verzögerung für Nutzer unsichtbar und verwirrend.
Die registrierte registerType: 'prompt'-Option aus Abschnitt zwei gibt der Anwendung die Kontrolle zurück: Statt den Service Worker automatisch zu aktualisieren, liefert vite-plugin-pwa ein virtuelles Modul, über das die Vue-Anwendung erkennt, wann eine neue Version verfügbar ist, und dem Nutzer aktiv einen Hinweis anzeigen kann, bevor die Aktualisierung angestoßen wird.
// composables/usePwaUpdate.js
import { ref } from 'vue'
import { registerSW } from 'virtual:pwa-register'
export function usePwaUpdate() {
const updateAvailable = ref(false)
let updateSW
updateSW = registerSW({
onNeedRefresh() {
updateAvailable.value = true // show a toast/banner in the UI
},
onOfflineReady() {
console.log('App ready to work offline')
},
})
function applyUpdate() {
updateSW(true) // reloads the page with the new service worker active
}
return { updateAvailable, applyUpdate }
}
In der Praxis zeigt man bei updateAvailable === true einen dezenten Banner mit einem Text wie "Neue Version verfügbar" und einem Button, der applyUpdate auslöst. Dieses Muster stellt sicher, dass Nutzer einer Vue-PWA nicht überraschend während einer laufenden Eingabe unterbrochen werden, sondern selbst entscheiden, wann der Reload für die neue Version stattfindet.
7. API-Daten offline verfügbar machen mit IndexedDB
Reines HTTP-Caching über den Service Worker reicht für einfache, wiederholbare GET-Anfragen aus, stößt aber an Grenzen, sobald eine Vue-PWA strukturierte Daten offline durchsuchbar oder filterbar machen soll. IndexedDB ist die Browser-Datenbank der Wahl für solche Fälle, weil sie im Gegensatz zu localStorage größere Datenmengen, Indizes für Abfragen und asynchrone Zugriffe unterstützt.
Die Bibliothek Dexie.js vereinfacht die Arbeit mit IndexedDB erheblich gegenüber der rohen, recht umständlichen nativen API. In Kombination mit einem Pinia-Store lässt sich ein Muster etablieren, bei dem Daten zuerst aus IndexedDB geladen werden, während im Hintergrund eine aktuelle Version vom Server nachgeladen und der lokale Datenbestand aktualisiert wird.
// stores/useProductStore.js — Pinia store backed by IndexedDB via Dexie
import { defineStore } from 'pinia'
import Dexie from 'dexie'
const db = new Dexie('mironsoft-db')
db.version(1).stores({ products: 'id, name, category' })
export const useProductStore = defineStore('products', {
state: () => ({ products: [] }),
actions: {
async loadProducts() {
// Show cached data immediately, even fully offline
this.products = await db.products.toArray()
try {
const response = await fetch('https://api.mironsoft.de/products')
const fresh = await response.json()
this.products = fresh
await db.products.clear()
await db.products.bulkPut(fresh) // persist for the next offline session
} catch {
// Network failed, the IndexedDB snapshot from above stays in use
}
},
},
})
8. Installation-Prompts und Nutzerführung
Chrome und Edge lösen das beforeinstallprompt-Event aus, sobald ihre internen Heuristiken eine Vue-PWA als installationswürdig einstufen, üblicherweise nach wiederholten Besuchen und einem gültigen Manifest samt Service Worker. Dieses Event lässt sich abfangen, um einen eigenen, ins Design integrierten Install-Button anzubieten, statt sich auf den browsereigenen Install-Hinweis zu verlassen, der leicht übersehen wird.
Safari auf iOS unterstützt dieses Event nicht und bietet stattdessen nur den manuellen Weg über "Zum Home-Bildschirm hinzufügen" im Teilen-Menü. Für iOS-Nutzer lohnt sich daher eine dezente, kontextsensitive Anleitung, die genau diesen Weg erklärt, weil ohne Hinweis viele Nutzer die Installationsmöglichkeit auf iOS schlicht nicht entdecken.
// composables/useInstallPrompt.js
import { ref, onMounted } from 'vue'
export function useInstallPrompt() {
const deferredPrompt = ref(null)
const canInstall = ref(false)
onMounted(() => {
window.addEventListener('beforeinstallprompt', (event) => {
event.preventDefault() // suppress the browser's default mini-infobar
deferredPrompt.value = event
canInstall.value = true
})
})
async function promptInstall() {
if (!deferredPrompt.value) return
deferredPrompt.value.prompt()
const { outcome } = await deferredPrompt.value.userChoice
canInstall.value = false
deferredPrompt.value = null
return outcome // 'accepted' or 'dismissed'
}
return { canInstall, promptInstall }
}
9. PWA im Vergleich zu Capacitor für native Distribution
Eine PWA mit Vue ist nicht immer die richtige Wahl für jedes Projekt. Die Entscheidung zwischen PWA und einem nativen Wrapper wie Capacitor hängt stark davon ab, welche Vertriebswege und welche native Funktionalität tatsächlich benötigt werden.
| Kriterium | PWA mit Vue | Vue mit Capacitor |
|---|---|---|
| Distribution | Direkt per URL, kein Store-Review | App Store und Play Store, Review nötig |
| Update-Geschwindigkeit | Sofort nach Deployment | Store-Review-Zeit für native Updates |
| Native APIs | Begrenzt auf Web-APIs | Voller Zugriff über Plugins |
| iOS-Installierbarkeit | Nur manuell über Teilen-Menü | Regulärer App-Store-Eintrag |
| Entwicklungsaufwand | Minimal, gleiche Codebasis | Zusätzlicher nativer Build-Prozess |
Für interne Business-Tools, Content-lastige Anwendungen oder Projekte mit begrenztem Budget ist eine PWA mit Vue oft die schnellere und günstigere Lösung, weil weder Store-Gebühren noch Review-Prozesse anfallen und Updates sofort bei allen Nutzern ankommen. Sobald ein Projekt jedoch auf Store-Präsenz für Marketing-Zwecke angewiesen ist oder tiefen Zugriff auf native Hardware-Funktionen benötigt, ist Capacitor die passendere Wahl, wie im ersten Artikel dieser Reihe beschrieben.
Mironsoft
Vue-Entwicklung, Progressive Web Apps und Offline-Architektur
Ihre Vue-Anwendung installierbar und offline-fähig?
Wir richten Service Worker, Manifest und Caching-Strategien für Ihre Vue-Anwendung ein, konfigurieren sauberes Update-Handling und sorgen für ein durchdachtes Offline-Erlebnis statt zufälligem Verhalten.
PWA-Setup
vite-plugin-pwa konfigurieren, Manifest und Icons bereitstellen
Caching-Strategie
Passende Strategie pro Ressourcentyp fachlich und technisch festlegen
Offline-UX
Fallback-Seiten, Statusanzeigen und Update-Banner umsetzen
10. Zusammenfassung
Eine PWA mit Vue verwandelt eine bestehende Vue-Anwendung mit vergleichsweise geringem Aufwand in eine installierbare, offline-fähige App. vite-plugin-pwa automatisiert die Generierung von Service Worker und Web-App-Manifest, während Workbox-basierte Caching-Strategien wie Cache First, Network First und Stale While Revalidate je nach Ressourcentyp gezielt konfiguriert werden sollten, statt eine einzige Strategie für alles zu verwenden.
Durchdachtes Offline-Verhalten mit dedizierter Fallback-Seite, aktives Update-Handling mit Nutzerbenachrichtigung statt stiller Aktualisierung, und IndexedDB für offline durchsuchbare Daten heben eine Vue-PWA von einer bloßen technischen Übung zu einer tatsächlich nutzbaren Offline-Erfahrung. Gegenüber Capacitor bleibt die PWA die schnellere, store-freie Alternative für Projekte ohne Bedarf an tiefem nativem Funktionszugriff.
PWA mit Vue und Vite — Das Wichtigste auf einen Blick
Setup
vite-plugin-pwa generiert Service Worker und Manifest automatisch bei jedem Build.
Caching-Strategien
Cache First für statische Assets, Network First oder Stale While Revalidate für API-Daten.
Update-Handling
registerType: prompt gibt der App die Kontrolle über den Zeitpunkt der Aktualisierung.
Offline-Daten
IndexedDB via Dexie.js für strukturierte, offline durchsuchbare API-Daten.