139 lines
3.9 KiB
JavaScript
139 lines
3.9 KiB
JavaScript
/**
|
||
* 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' })
|
||
}
|
||
})
|
||
)
|
||
}
|
||
})
|