优化服务工作者

This commit is contained in:
jxxghp
2025-07-07 22:56:36 +08:00
parent 27c7a842db
commit 9fc5291fec
+55 -39
View File
@@ -155,14 +155,14 @@ async function clearBadge() {
async function deleteOldCaches() { async function deleteOldCaches() {
const cacheWhitelist = Object.values(CACHE_NAMES) const cacheWhitelist = Object.values(CACHE_NAMES)
const cacheNames = await caches.keys() const cacheNames = await caches.keys()
await Promise.all( await Promise.all(
cacheNames.map(async (cacheName) => { cacheNames.map(async cacheName => {
if (!cacheWhitelist.includes(cacheName)) { if (!cacheWhitelist.includes(cacheName)) {
console.log('Deleting old cache:', cacheName) console.log('Deleting old cache:', cacheName)
return caches.delete(cacheName) return caches.delete(cacheName)
} }
}) }),
) )
} }
@@ -171,12 +171,12 @@ async function getCacheSize(cacheName: string): Promise<number> {
if (!('estimate' in navigator.storage)) { if (!('estimate' in navigator.storage)) {
return 0 return 0
} }
try { try {
const cache = await caches.open(cacheName) const cache = await caches.open(cacheName)
const keys = await cache.keys() const keys = await cache.keys()
let totalSize = 0 let totalSize = 0
for (const request of keys) { for (const request of keys) {
const response = await cache.match(request) const response = await cache.match(request)
if (response) { if (response) {
@@ -184,7 +184,7 @@ async function getCacheSize(cacheName: string): Promise<number> {
totalSize += blob.size totalSize += blob.size
} }
} }
return totalSize return totalSize
} catch (error) { } catch (error) {
console.error('Failed to get cache size:', error) console.error('Failed to get cache size:', error)
@@ -196,13 +196,13 @@ async function getCacheSize(cacheName: string): Promise<number> {
async function monitorCacheSize() { async function monitorCacheSize() {
const cacheSizes: Record<string, number> = {} const cacheSizes: Record<string, number> = {}
let totalSize = 0 let totalSize = 0
for (const [key, cacheName] of Object.entries(CACHE_NAMES)) { for (const [key, cacheName] of Object.entries(CACHE_NAMES)) {
const size = await getCacheSize(cacheName) const size = await getCacheSize(cacheName)
cacheSizes[key] = size cacheSizes[key] = size
totalSize += size totalSize += size
} }
// 发送缓存统计信息给客户端 // 发送缓存统计信息给客户端
const clients = await self.clients.matchAll() const clients = await self.clients.matchAll()
clients.forEach(client => { clients.forEach(client => {
@@ -212,10 +212,10 @@ async function monitorCacheSize() {
cacheSizes, cacheSizes,
totalSize, totalSize,
totalSizeMB: (totalSize / 1024 / 1024).toFixed(2), totalSizeMB: (totalSize / 1024 / 1024).toFixed(2),
} },
}) })
}) })
return { cacheSizes, totalSize } return { cacheSizes, totalSize }
} }
@@ -224,16 +224,16 @@ async function cleanupExpiredCaches() {
for (const [key, cacheName] of Object.entries(CACHE_NAMES)) { for (const [key, cacheName] of Object.entries(CACHE_NAMES)) {
const limit = CACHE_SIZE_LIMITS[key as keyof typeof CACHE_SIZE_LIMITS] const limit = CACHE_SIZE_LIMITS[key as keyof typeof CACHE_SIZE_LIMITS]
if (!limit) continue if (!limit) continue
try { try {
const cache = await caches.open(cacheName) const cache = await caches.open(cacheName)
const keys = await cache.keys() const keys = await cache.keys()
// 如果缓存条目超过限制,删除最老的条目 // 如果缓存条目超过限制,删除最老的条目
if (keys.length > limit.maxEntries) { if (keys.length > limit.maxEntries) {
const deleteCount = keys.length - limit.maxEntries const deleteCount = keys.length - limit.maxEntries
console.log(`Cleaning up ${deleteCount} entries from ${cacheName}`) console.log(`Cleaning up ${deleteCount} entries from ${cacheName}`)
// 删除最老的条目(假设数组开头是最老的) // 删除最老的条目(假设数组开头是最老的)
for (let i = 0; i < deleteCount; i++) { for (let i = 0; i < deleteCount; i++) {
await cache.delete(keys[i]) await cache.delete(keys[i])
@@ -262,10 +262,10 @@ self.addEventListener('activate', event => {
// 清理旧版本的缓存 // 清理旧版本的缓存
await deleteOldCaches() await deleteOldCaches()
// 清理过期的缓存条目 // 清理过期的缓存条目
await cleanupExpiredCaches() await cleanupExpiredCaches()
// 监控缓存大小 // 监控缓存大小
await monitorCacheSize() await monitorCacheSize()
})(), })(),
@@ -313,7 +313,7 @@ self.addEventListener('fetch', event => {
} }
})(), })(),
) )
} }
// POST/PUT/DELETE请求:离线时加入同步队列 // POST/PUT/DELETE请求:离线时加入同步队列
else if (['POST', 'PUT', 'DELETE', 'PATCH'].includes(event.request.method)) { else if (['POST', 'PUT', 'DELETE', 'PATCH'].includes(event.request.method)) {
event.respondWith( event.respondWith(
@@ -325,7 +325,7 @@ self.addEventListener('fetch', event => {
} catch (error) { } catch (error) {
// 网络错误时,加入同步队列 // 网络错误时,加入同步队列
await addToSyncQueue(event.request) await addToSyncQueue(event.request)
// 通知客户端请求已加入队列 // 通知客户端请求已加入队列
if (self.clients) { if (self.clients) {
self.clients.matchAll().then(clients => { self.clients.matchAll().then(clients => {
@@ -338,18 +338,18 @@ self.addEventListener('fetch', event => {
}) })
}) })
} }
// 返回一个假的成功响应 // 返回一个假的成功响应
return new Response( return new Response(
JSON.stringify({ JSON.stringify({
success: true, success: true,
queued: true, queued: true,
message: '请求已加入离线队列,将在网络恢复后自动同步' message: '请求已加入离线队列,将在网络恢复后自动同步',
}), }),
{ {
status: 202, status: 202,
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
} },
) )
} }
})(), })(),
@@ -373,7 +373,7 @@ async function addToSyncQueue(request: Request) {
const id = `${Date.now()}-${Math.random().toString(36).substr(2, 9)}` const id = `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`
const url = request.url const url = request.url
const method = request.method const method = request.method
let data: any = null let data: any = null
if (method !== 'GET' && method !== 'HEAD') { if (method !== 'GET' && method !== 'HEAD') {
try { try {
@@ -382,7 +382,7 @@ async function addToSyncQueue(request: Request) {
console.error('Failed to read request body:', e) console.error('Failed to read request body:', e)
} }
} }
const syncItem = { const syncItem = {
id, id,
url, url,
@@ -390,11 +390,11 @@ async function addToSyncQueue(request: Request) {
data, data,
timestamp: Date.now(), timestamp: Date.now(),
} }
// 保存到IndexedDB (使用专用的 "sync" store) // 保存到IndexedDB (使用专用的 "sync" store)
await set(id, syncItem, 'sync') await set(id, syncItem, 'sync')
syncQueue.push(syncItem) syncQueue.push(syncItem)
// 注册后台同步 // 注册后台同步
if ('sync' in self.registration) { if ('sync' in self.registration) {
await self.registration.sync.register('sync-data') await self.registration.sync.register('sync-data')
@@ -405,16 +405,19 @@ async function addToSyncQueue(request: Request) {
async function processSyncQueue() { async function processSyncQueue() {
const db = await openDB() const db = await openDB()
// 使用专用的 "sync" store,并开启 readwrite 事务(便于后续删除) // 先用只读事务获取所有同步项
const tx = db.transaction(['sync'], 'readwrite')
const store = tx.objectStore('sync')
const items: Array<any> = await new Promise((resolve, reject) => { const items: Array<any> = await new Promise((resolve, reject) => {
const tx = db.transaction(['sync'], 'readonly')
const store = tx.objectStore('sync')
const req = store.getAll() const req = store.getAll()
req.onsuccess = () => resolve(req.result) req.onsuccess = () => resolve(req.result)
req.onerror = () => reject(req.error) req.onerror = () => reject(req.error)
}) })
// 收集需要删除的项目ID
const itemsToDelete: string[] = []
const itemsToDeleteExpired: string[] = []
for (const syncItem of items) { for (const syncItem of items) {
const key = syncItem.id const key = syncItem.id
try { try {
@@ -434,8 +437,8 @@ async function processSyncQueue() {
const response = await fetch(syncItem.url, init) const response = await fetch(syncItem.url, init)
if (response.ok) { if (response.ok) {
// 成功后删除同步项,并等待事务完成 // 成功后标记为需要删除
await del(key, 'sync') itemsToDelete.push(key)
// 通知客户端同步成功 // 通知客户端同步成功
const clients = await self.clients.matchAll() const clients = await self.clients.matchAll()
@@ -452,12 +455,29 @@ async function processSyncQueue() {
} catch (error) { } catch (error) {
console.error('Sync failed for item:', key, error) console.error('Sync failed for item:', key, error)
// 如果该同步项已存在超过 24 小时,则将其丢弃 // 如果该同步项已存在超过 24 小时,则标记为需要删除
if (Date.now() - syncItem.timestamp > 24 * 60 * 60 * 1000) { if (Date.now() - syncItem.timestamp > 24 * 60 * 60 * 1000) {
await del(key, 'sync') itemsToDeleteExpired.push(key)
} }
} }
} }
// 批量删除所有成功处理的项目和过期项目
const allItemsToDelete = [...itemsToDelete, ...itemsToDeleteExpired]
if (allItemsToDelete.length > 0) {
await new Promise<void>((resolve, reject) => {
const tx = db.transaction(['sync'], 'readwrite')
const store = tx.objectStore('sync')
// 批量删除所有标记的项目
allItemsToDelete.forEach(id => {
store.delete(id)
})
tx.oncomplete = () => resolve()
tx.onerror = () => reject(tx.error)
})
}
} }
// 初始化 Workbox // 初始化 Workbox
@@ -552,11 +572,7 @@ self.addEventListener('message', function (event) {
}) })
} else if (event.data && event.data.type === 'CLEANUP_CACHES') { } else if (event.data && event.data.type === 'CLEANUP_CACHES') {
// 手动触发缓存清理 // 手动触发缓存清理
Promise.all([ Promise.all([deleteOldCaches(), cleanupExpiredCaches(), monitorCacheSize()])
deleteOldCaches(),
cleanupExpiredCaches(),
monitorCacheSize()
])
.then(([, , cacheInfo]) => { .then(([, , cacheInfo]) => {
event.ports[0]?.postMessage({ success: true, cacheInfo }) event.ports[0]?.postMessage({ success: true, cacheInfo })
}) })