perf: Schritt 32 — Lazy Loading & Caching-Strategie
Cache-Control (server/index.js):
- Bilder/Fonts: public, max-age=2592000, immutable (30 Tage)
- HTML/JS/CSS/JSON: no-cache, must-revalidate (ETag-Revalidierung)
→ Deployment-Updates greifen sofort, unveränderte Dateien = 304 ohne Transfer
- API: no-store (kein Browser-Caching von Nutzerdaten)
Service Worker (public/sw.js → v3):
- Drei getrennte Caches: shell-v3, pages-v3, assets-v3
- App-Shell + alle Seiten-Module beim Install vorab gecacht
- Stale-While-Revalidate für App-Shell + Seiten-JS:
sofortiger Render aus Cache, Hintergrund-Update ohne Blockierung
- Cache-First für Bilder/Fonts (seltene Änderungen)
- postMessage({ type: 'SW_UPDATED' }) bei Aktivierung neuer Version
Modul-Cache + Update-Toast (public/router.js):
- moduleCache Map: dynamische imports werden einmalig gespeichert,
wiederholte Navigation braucht keinen Import-Lookup mehr
- SW_UPDATED-Handler: leert moduleCache + zeigt Update-Toast (8s)
Preconnect + Preload (public/index.html):
- <link rel="preconnect" href="https://unpkg.com">
- <link rel="dns-prefetch" href="https://openweathermap.org">
- <link rel="preload" href="/api.js" as="script">
- <link rel="preload" href="/router.js" as="script">
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+28
-1
@@ -23,6 +23,18 @@ const ROUTES = [
|
||||
{ path: '/settings', page: '/pages/settings.js', requiresAuth: true },
|
||||
];
|
||||
|
||||
// --------------------------------------------------------
|
||||
// Modul-Cache: verhindert redundante dynamic imports bei Navigation
|
||||
// --------------------------------------------------------
|
||||
const moduleCache = new Map();
|
||||
|
||||
async function importPage(pagePath) {
|
||||
if (!moduleCache.has(pagePath)) {
|
||||
moduleCache.set(pagePath, await import(pagePath));
|
||||
}
|
||||
return moduleCache.get(pagePath);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------
|
||||
// Globaler App-State
|
||||
// --------------------------------------------------------
|
||||
@@ -80,7 +92,7 @@ async function renderPage(route) {
|
||||
if (loading) loading.hidden = true;
|
||||
|
||||
try {
|
||||
const module = await import(route.page + '?v=1');
|
||||
const module = await importPage(route.page);
|
||||
|
||||
if (typeof module.render !== 'function') {
|
||||
throw new Error(`Seite ${route.page} exportiert keine render()-Funktion.`);
|
||||
@@ -237,6 +249,21 @@ window.addEventListener('unhandledrejection', (e) => {
|
||||
e.preventDefault(); // Konsolenfehler unterdrücken (bereits geloggt)
|
||||
});
|
||||
|
||||
// SW-Update: neue Version im Hintergrund installiert → Toast anzeigen
|
||||
if ('serviceWorker' in navigator) {
|
||||
navigator.serviceWorker.addEventListener('message', (e) => {
|
||||
if (e.data?.type === 'SW_UPDATED') {
|
||||
// Modul-Cache leeren damit nächste Navigation frische Module lädt
|
||||
moduleCache.clear();
|
||||
showToast(
|
||||
'Update verfügbar — Seite neu laden für die neueste Version.',
|
||||
'default',
|
||||
8000
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Browser zurück/vor
|
||||
window.addEventListener('popstate', (e) => {
|
||||
navigate(e.state?.path || location.pathname, false);
|
||||
|
||||
Reference in New Issue
Block a user