初始化
Some checks failed
Some checks failed
This commit is contained in:
287
frontend/public/sw.js
Normal file
287
frontend/public/sw.js
Normal file
@@ -0,0 +1,287 @@
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user