Files
it/frontend/public/sw.js

287 lines
7.1 KiB
JavaScript
Raw Normal View History

2025-11-03 19:47:36 +08:00
// Service Worker for PWA functionality
const CACHE_NAME = 'it-hardware-ranking-v1'
const RUNTIME_CACHE = 'it-hardware-ranking-runtime'
// 需要预缓存的资源列表
const STATIC_CACHE_URLS = [
'/',
'/index.html',
'/manifest.json',
'/favicon.ico',
// 添加其他需要预缓存的静态资源
]
// 需要网络优先的资源
const NETWORK_FIRST_URLS = [
'/api/',
// 添加其他需要网络优先的API路径
]
// 需要缓存优先的资源
const CACHE_FIRST_URLS = [
'/static/',
'/assets/',
// 添加其他需要缓存优先的静态资源路径
]
// 安装事件 - 预缓存静态资源
self.addEventListener('install', (event) => {
console.log('[SW] Install event triggered')
event.waitUntil(
caches.open(CACHE_NAME)
.then((cache) => {
console.log('[SW] Caching static resources')
return cache.addAll(STATIC_CACHE_URLS)
})
.then(() => {
console.log('[SW] Static resources cached successfully')
// 强制激活新的Service Worker
return self.skipWaiting()
})
.catch((error) => {
console.error('[SW] Failed to cache static resources:', error)
})
)
})
// 激活事件 - 清理旧缓存
self.addEventListener('activate', (event) => {
console.log('[SW] Activate event triggered')
event.waitUntil(
caches.keys()
.then((cacheNames) => {
return Promise.all(
cacheNames.map((cacheName) => {
// 删除旧版本的缓存
if (cacheName !== CACHE_NAME && cacheName !== RUNTIME_CACHE) {
console.log('[SW] Deleting old cache:', cacheName)
return caches.delete(cacheName)
}
})
)
})
.then(() => {
console.log('[SW] Old caches cleaned up')
// 立即控制所有客户端
return self.clients.claim()
})
.catch((error) => {
console.error('[SW] Failed to clean up old caches:', error)
})
)
})
// 网络请求拦截
self.addEventListener('fetch', (event) => {
const { request } = event
const url = new URL(request.url)
// 跳过非HTTP(S)请求
if (!url.protocol.startsWith('http')) {
return
}
// 跳过Chrome扩展请求
if (url.protocol === 'chrome-extension:') {
return
}
// 根据请求URL选择缓存策略
if (isNetworkFirst(url)) {
// 网络优先策略
event.respondWith(networkFirst(request))
} else if (isCacheFirst(url)) {
// 缓存优先策略
event.respondWith(cacheFirst(request))
} else {
// 缓存优先,网络作为后备策略
event.respondWith(staleWhileRevalidate(request))
}
})
// 判断是否使用网络优先策略
function isNetworkFirst(url) {
return NETWORK_FIRST_URLS.some(path => url.pathname.startsWith(path))
}
// 判断是否使用缓存优先策略
function isCacheFirst(url) {
return CACHE_FIRST_URLS.some(path => url.pathname.startsWith(path))
}
// 网络优先策略
async function networkFirst(request) {
const cache = await caches.open(RUNTIME_CACHE)
try {
// 尝试从网络获取
const response = await fetch(request)
// 如果响应成功,缓存它
if (response.ok) {
cache.put(request, response.clone())
}
return response
} catch (error) {
console.log('[SW] Network request failed, trying cache:', error)
// 网络失败,尝试从缓存获取
const cachedResponse = await cache.match(request)
if (cachedResponse) {
return cachedResponse
}
// 如果缓存也没有,返回离线页面
return new Response('离线状态,请检查网络连接', {
status: 503,
statusText: 'Service Unavailable'
})
}
}
// 缓存优先策略
async function cacheFirst(request) {
const cache = await caches.open(RUNTIME_CACHE)
const cachedResponse = await cache.match(request)
if (cachedResponse) {
return cachedResponse
}
try {
// 缓存中没有,从网络获取
const response = await fetch(request)
// 如果响应成功,缓存它
if (response.ok) {
cache.put(request, response.clone())
}
return response
} catch (error) {
console.log('[SW] Network request failed:', error)
// 返回错误响应
return new Response('网络请求失败', {
status: 500,
statusText: 'Internal Server Error'
})
}
}
// 缓存优先,网络作为后备策略
async function staleWhileRevalidate(request) {
const cache = await caches.open(RUNTIME_CACHE)
const cachedResponse = await cache.match(request)
// 在后台发起网络请求
const fetchPromise = fetch(request).then((response) => {
// 如果响应成功,更新缓存
if (response.ok) {
cache.put(request, response.clone())
}
return response
}).catch((error) => {
console.log('[SW] Background fetch failed:', error)
// 返回错误响应,但不影响缓存的响应
return new Response('网络请求失败', {
status: 500,
statusText: 'Internal Server Error'
})
})
// 如果有缓存,立即返回缓存
if (cachedResponse) {
return cachedResponse
}
// 没有缓存,等待网络请求
return fetchPromise
}
// 后台同步事件
self.addEventListener('sync', (event) => {
console.log('[SW] Background sync event:', event.tag)
if (event.tag === 'background-sync') {
event.waitUntil(doBackgroundSync())
}
})
// 执行后台同步
async function doBackgroundSync() {
try {
// 这里可以执行需要在网络恢复时同步的任务
console.log('[SW] Performing background sync')
// 例如:同步离线时的数据
// await syncOfflineData()
} catch (error) {
console.error('[SW] Background sync failed:', error)
}
}
// 推送通知事件
self.addEventListener('push', (event) => {
console.log('[SW] Push event received')
if (!event.data) {
return
}
const options = event.data.json()
event.waitUntil(
self.registration.showNotification(options.title || '新消息', {
body: options.body || '您有新消息',
icon: options.icon || '/favicon.ico',
badge: options.badge || '/favicon.ico',
data: options.data || {},
actions: options.actions || []
})
)
})
// 通知点击事件
self.addEventListener('notificationclick', (event) => {
console.log('[SW] Notification click event')
event.notification.close()
// 处理通知点击
if (event.action) {
// 处理特定的操作按钮点击
handleNotificationAction(event.action, event.notification.data)
} else {
// 处理通知主体点击
handleNotificationClick(event.notification.data)
}
})
// 处理通知点击
function handleNotificationClick(data) {
// 打开应用或特定页面
event.waitUntil(
clients.openWindow(data.url || '/')
)
}
// 处理通知操作
function handleNotificationAction(action, data) {
// 根据不同的操作执行不同的逻辑
switch (action) {
case 'view':
clients.openWindow(data.url || '/')
break
case 'dismiss':
// 关闭通知,无需其他操作
break
default:
console.log('[SW] Unknown notification action:', action)
}
}