chore: sync project updates
This commit is contained in:
146
mengyakeyvault-frontend/public/service-worker.js
Normal file
146
mengyakeyvault-frontend/public/service-worker.js
Normal file
@@ -0,0 +1,146 @@
|
||||
/* =====================================================
|
||||
* 萌芽密码管理器 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();
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user