chore: sync local changes (2026-03-12)
This commit is contained in:
@@ -1,48 +1,150 @@
|
||||
// Service Worker for InfoGenie App
|
||||
const CACHE_NAME = 'infogenie-cache-v1';
|
||||
const urlsToCache = [
|
||||
// Service Worker for InfoGenie App (PWA)
|
||||
// 注意:PWA 必须在 HTTPS(或 localhost)下才能生效
|
||||
|
||||
const CACHE_VERSION = 'v2';
|
||||
const PRECACHE_NAME = `infogenie-precache-${CACHE_VERSION}`;
|
||||
const RUNTIME_NAME = `infogenie-runtime-${CACHE_VERSION}`;
|
||||
|
||||
const CORE_ASSETS = [
|
||||
'/',
|
||||
'/index.html',
|
||||
'/manifest.json'
|
||||
'/manifest.json',
|
||||
'/icons/icon-192.png',
|
||||
'/icons/icon-512.png',
|
||||
'/icons/icon-192-maskable.png',
|
||||
'/icons/icon-512-maskable.png',
|
||||
'/icons/apple-touch-icon.png',
|
||||
'/icons/favicon-32.png',
|
||||
'/icons/favicon-16.png'
|
||||
];
|
||||
|
||||
// 安装Service Worker
|
||||
async function safeAddAll(cache, urls) {
|
||||
const uniqueUrls = Array.from(new Set(urls)).filter(Boolean);
|
||||
const results = await Promise.allSettled(uniqueUrls.map(url => cache.add(url)));
|
||||
const failures = results.filter(r => r.status === 'rejected');
|
||||
if (failures.length > 0) {
|
||||
console.warn('[SW] Some assets failed to cache:', failures.length);
|
||||
}
|
||||
}
|
||||
|
||||
async function precacheEntrypoints(cache) {
|
||||
try {
|
||||
const res = await fetch('/asset-manifest.json', { cache: 'no-store' });
|
||||
if (!res.ok) return;
|
||||
const manifest = await res.json();
|
||||
|
||||
const filesObj = manifest && typeof manifest === 'object' ? manifest.files : undefined;
|
||||
const files = filesObj && typeof filesObj === 'object' ? Object.values(filesObj) : [];
|
||||
const entrypoints = Array.isArray(manifest.entrypoints) ? manifest.entrypoints : [];
|
||||
|
||||
const urls = [...files, ...entrypoints]
|
||||
.filter(p => typeof p === 'string')
|
||||
.map(p => (p.startsWith('/') ? p : `/${p}`))
|
||||
.filter(p => !p.endsWith('.map'));
|
||||
|
||||
await safeAddAll(cache, urls);
|
||||
} catch (err) {
|
||||
console.warn('[SW] Failed to precache entrypoints:', err);
|
||||
}
|
||||
}
|
||||
|
||||
self.addEventListener('install', event => {
|
||||
self.skipWaiting();
|
||||
event.waitUntil(
|
||||
caches.open(CACHE_NAME)
|
||||
.then(cache => {
|
||||
console.log('Opened cache');
|
||||
return cache.addAll(urlsToCache);
|
||||
})
|
||||
(async () => {
|
||||
const cache = await caches.open(PRECACHE_NAME);
|
||||
await safeAddAll(cache, CORE_ASSETS);
|
||||
await precacheEntrypoints(cache);
|
||||
})()
|
||||
);
|
||||
});
|
||||
|
||||
// 拦截请求并从缓存中响应
|
||||
self.addEventListener('fetch', event => {
|
||||
event.respondWith(
|
||||
caches.match(event.request)
|
||||
.then(response => {
|
||||
// 如果找到缓存的响应,则返回缓存
|
||||
if (response) {
|
||||
return response;
|
||||
}
|
||||
return fetch(event.request);
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
// 更新Service Worker
|
||||
self.addEventListener('activate', event => {
|
||||
const cacheWhitelist = [CACHE_NAME];
|
||||
event.waitUntil(
|
||||
caches.keys().then(cacheNames => {
|
||||
return Promise.all(
|
||||
cacheNames.map(cacheName => {
|
||||
if (cacheWhitelist.indexOf(cacheName) === -1) {
|
||||
return caches.delete(cacheName);
|
||||
(async () => {
|
||||
const cacheNames = await caches.keys();
|
||||
await Promise.all(
|
||||
cacheNames.map(name => {
|
||||
if (name !== PRECACHE_NAME && name !== RUNTIME_NAME) {
|
||||
return caches.delete(name);
|
||||
}
|
||||
return undefined;
|
||||
})
|
||||
);
|
||||
})
|
||||
|
||||
await self.clients.claim();
|
||||
})()
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
function isNavigationRequest(request) {
|
||||
return request.mode === 'navigate' || request.destination === 'document';
|
||||
}
|
||||
|
||||
function shouldHandleRequest(url, request) {
|
||||
if (request.method !== 'GET') return false;
|
||||
if (url.origin !== self.location.origin) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
self.addEventListener('fetch', event => {
|
||||
const url = new URL(event.request.url);
|
||||
if (!shouldHandleRequest(url, event.request)) return;
|
||||
|
||||
// 不缓存后端 API(如需缓存请在这里加规则)
|
||||
if (url.pathname.startsWith('/api')) return;
|
||||
|
||||
// 页面请求:优先网络,离线回退到缓存
|
||||
if (isNavigationRequest(event.request)) {
|
||||
event.respondWith(
|
||||
(async () => {
|
||||
try {
|
||||
const networkResponse = await fetch(event.request);
|
||||
const cache = await caches.open(RUNTIME_NAME);
|
||||
cache.put(event.request, networkResponse.clone());
|
||||
return networkResponse;
|
||||
} catch (err) {
|
||||
const cached = await caches.match(event.request);
|
||||
return cached || caches.match('/index.html');
|
||||
}
|
||||
})()
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// 静态资源:stale-while-revalidate
|
||||
event.respondWith(
|
||||
(async () => {
|
||||
const cached = await caches.match(event.request);
|
||||
const cache = await caches.open(RUNTIME_NAME);
|
||||
|
||||
const networkFetch = (async () => {
|
||||
try {
|
||||
const response = await fetch(event.request);
|
||||
if (response && response.ok) {
|
||||
cache.put(event.request, response.clone());
|
||||
}
|
||||
return response;
|
||||
} catch (err) {
|
||||
return undefined;
|
||||
}
|
||||
})();
|
||||
|
||||
if (cached) {
|
||||
event.waitUntil(networkFetch);
|
||||
return cached;
|
||||
}
|
||||
|
||||
const response = await networkFetch;
|
||||
if (response) return response;
|
||||
|
||||
return new Response('', { status: 504, statusText: 'Offline' });
|
||||
})()
|
||||
);
|
||||
});
|
||||
|
||||
self.addEventListener('message', event => {
|
||||
if (event.data && event.data.type === 'SKIP_WAITING') {
|
||||
self.skipWaiting();
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user