Files
2026-06-20 14:26:29 +08:00

139 lines
3.9 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 - 静态资源缓存
* 提升二次加载速度,支持离线访问静态资源
*/
const CACHE_NAME = 'incudal-cache-v2'
// 需要缓存的静态资源类型
const CACHEABLE_EXTENSIONS = ['.js', '.css', '.woff', '.woff2', '.ttf', '.svg', '.png', '.jpg', '.jpeg', '.gif', '.ico']
// 需要始终从网络获取的路径(不缓存)
const NETWORK_ONLY_PATTERNS = [
'/api/', // API 请求
'/auth/', // 认证请求
'/oauth/', // OAuth 请求
'/__vite_ping', // Vite HMR
'/sw.js' // Service Worker 自身
]
// 安装事件
self.addEventListener('install', (event) => {
console.log('[SW] Installing service worker...')
// 跳过等待,立即激活
event.waitUntil(self.skipWaiting())
})
// 激活事件
self.addEventListener('activate', (event) => {
console.log('[SW] Activating service worker...')
event.waitUntil(
Promise.all([
// 清理旧版本缓存
caches.keys().then(cacheNames => {
return Promise.all(
cacheNames
.filter(cacheName => cacheName !== CACHE_NAME)
.map(cacheName => {
console.log('[SW] Deleting old cache:', cacheName)
return caches.delete(cacheName)
})
)
}),
// 立即接管所有客户端
self.clients.claim()
])
)
})
// 判断是否应该缓存该请求
function shouldCache(url) {
// 检查是否是网络优先的路径
for (const pattern of NETWORK_ONLY_PATTERNS) {
if (url.pathname.includes(pattern)) {
return false
}
}
// 只缓存同源请求
if (url.origin !== self.location.origin) {
return false
}
// 检查文件扩展名
const pathname = url.pathname.toLowerCase()
return CACHEABLE_EXTENSIONS.some(ext => pathname.endsWith(ext))
}
// 拦截请求
self.addEventListener('fetch', (event) => {
const url = new URL(event.request.url)
// 只处理 GET 请求
if (event.request.method !== 'GET') {
return
}
// 页面导航请求不做离线接管:
// 当前策略不缓存 HTML,如果这里兜底到缓存首页,会把瞬时网络抖动放大成“离线状态”。
if (event.request.mode === 'navigate') {
return
}
// 可缓存的静态资源 - 缓存优先策略(Stale While Revalidate
if (shouldCache(url)) {
event.respondWith(
caches.match(event.request).then(cachedResponse => {
if (cachedResponse) {
// 返回缓存,同时在后台更新
event.waitUntil(
fetch(event.request)
.then(networkResponse => {
if (networkResponse && networkResponse.status === 200) {
return caches.open(CACHE_NAME).then(cache => {
cache.put(event.request, networkResponse.clone())
})
}
})
.catch(() => {
// 后台更新失败,忽略
})
)
return cachedResponse
}
// 没有缓存,从网络获取并缓存
return fetch(event.request).then(networkResponse => {
if (networkResponse && networkResponse.status === 200) {
const responseToCache = networkResponse.clone()
event.waitUntil(
caches.open(CACHE_NAME).then(cache => {
cache.put(event.request, responseToCache)
})
)
}
return networkResponse
})
})
)
return
}
// 其他请求直接走网络
})
// 监听消息(用于手动清除缓存等操作)
self.addEventListener('message', (event) => {
if (event.data && event.data.type === 'CLEAR_CACHE') {
event.waitUntil(
caches.delete(CACHE_NAME).then(() => {
console.log('[SW] Cache cleared')
// 通知客户端
if (event.source && event.source.postMessage) {
event.source.postMessage({ type: 'CACHE_CLEARED' })
}
})
)
}
})