Files
2026-03-11 21:15:06 +08:00

147 lines
4.7 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* =====================================================
* 萌芽密码管理器 Service Worker
* 策略:
* - 静态资源Shell: Cache First优先缓存
* - API 请求: Network First优先网络失败时返回离线页
* - 导航请求: Network First → 回退到缓存的 index.html
* ===================================================== */
const CACHE_NAME = 'mengyakeyvault-v1';
const OFFLINE_URL = '/offline.html';
// 预缓存的应用 Shell 资源
const PRECACHE_URLS = [
'/',
'/index.html',
'/offline.html',
'/manifest.json',
'/favicon.ico',
'/logo.png',
];
// ── Install ──────────────────────────────────────────
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_NAME).then((cache) => {
return cache.addAll(PRECACHE_URLS).catch((err) => {
console.warn('[SW] 预缓存部分资源失败:', err);
});
})
);
// 强制新 SW 立即激活,不等旧 SW 退出
self.skipWaiting();
});
// ── Activate ─────────────────────────────────────────
self.addEventListener('activate', (event) => {
event.waitUntil(
caches.keys().then((cacheNames) => {
return Promise.all(
cacheNames
.filter((name) => name !== CACHE_NAME)
.map((name) => caches.delete(name))
);
})
);
// 立即接管所有页面
self.clients.claim();
});
// ── Fetch ─────────────────────────────────────────────
self.addEventListener('fetch', (event) => {
const { request } = event;
const url = new URL(request.url);
// 只处理 http/https忽略 chrome-extension 等
if (!url.protocol.startsWith('http')) return;
// API 请求Network First
if (url.pathname.startsWith('/api') || url.hostname.includes('keyvault.api')) {
event.respondWith(networkFirst(request));
return;
}
// 第三方资源favicon API 等Network First不缓存
if (url.hostname !== self.location.hostname) {
event.respondWith(networkOnly(request));
return;
}
// 导航请求HTML页面Network First → 回退 index.html
if (request.mode === 'navigate') {
event.respondWith(navigationHandler(request));
return;
}
// 静态资源JS/CSS/图片等Cache First
event.respondWith(cacheFirst(request));
});
// ── 策略函数 ──────────────────────────────────────────
// Cache First先查缓存没有再请求网络并写入缓存
async function cacheFirst(request) {
const cached = await caches.match(request);
if (cached) return cached;
try {
const response = await fetch(request);
if (response.ok) {
const cache = await caches.open(CACHE_NAME);
cache.put(request, response.clone());
}
return response;
} catch {
return new Response('资源暂时无法访问', { status: 503 });
}
}
// Network First先请求网络失败时查缓存
async function networkFirst(request) {
try {
const response = await fetch(request);
if (response.ok) {
const cache = await caches.open(CACHE_NAME);
cache.put(request, response.clone());
}
return response;
} catch {
const cached = await caches.match(request);
return cached || new Response(
JSON.stringify({ error: '网络不可用,请检查连接' }),
{ status: 503, headers: { 'Content-Type': 'application/json' } }
);
}
}
// Network Only仅网络不缓存
async function networkOnly(request) {
try {
return await fetch(request);
} catch {
return new Response('', { status: 503 });
}
}
// 导航处理Network First → 回退缓存的 index.html
async function navigationHandler(request) {
try {
const response = await fetch(request);
if (response.ok) {
const cache = await caches.open(CACHE_NAME);
cache.put(request, response.clone());
}
return response;
} catch {
const cached = await caches.match('/index.html');
if (cached) return cached;
return caches.match(OFFLINE_URL);
}
}
// ── 消息处理(支持主线程主动触发更新)──────────────────
self.addEventListener('message', (event) => {
if (event.data && event.data.type === 'SKIP_WAITING') {
self.skipWaiting();
}
});