diff --git a/src/service-worker.ts b/src/service-worker.ts index 6257bf62..46f782ae 100644 --- a/src/service-worker.ts +++ b/src/service-worker.ts @@ -1,5 +1,4 @@ import { cleanupOutdatedCaches, precacheAndRoute } from 'workbox-precaching' -import { NavigationRoute, registerRoute } from 'workbox-routing' declare let self: ServiceWorkerGlobalScope @@ -8,43 +7,10 @@ cleanupOutdatedCaches() // self.__WB_MANIFEST is default injection point precacheAndRoute(self.__WB_MANIFEST) -// 预缓存离线页面 -const OFFLINE_PAGE = '/offline.html' +// 离线版本控制 - 递增此版本号将触发install事件并强制更新缓存资源 +const OFFLINE_VERSION = 1 const CACHE_NAME = 'mp-offline-cache-v1' - -// 安装时缓存离线页面 -self.addEventListener('install', event => { - event.waitUntil( - (async () => { - const cache = await caches.open(CACHE_NAME) - await cache.add(OFFLINE_PAGE) - })(), - ) -}) - -// 处理导航请求的离线回退 -const navigationHandler = async (params: any) => { - try { - // 尝试从网络获取页面 - const response = await fetch(params.request) - return response - } catch (error) { - // 如果网络失败,返回离线页面 - const cache = await caches.open(CACHE_NAME) - const offlineResponse = await cache.match(OFFLINE_PAGE) - return offlineResponse || new Response('离线页面不可用', { status: 503 }) - } -} - -// 注册导航路由,排除API请求 -registerRoute( - new NavigationRoute(navigationHandler, { - denylist: [/^(\/[\w-]+)*\/api/, /\/offline\.html$/], - }), -) - -// 注意:静态资源和API的缓存策略已在vite.config.ts中的workbox配置中定义 -// 这里只处理离线页面的特殊逻辑 +const OFFLINE_URL = '/offline.html' // 通知选项 const options = { @@ -140,6 +106,66 @@ async function clearBadge() { } } +// 安装事件 - 缓存离线页面 +self.addEventListener('install', event => { + console.log('Service Worker install, version:', OFFLINE_VERSION) + event.waitUntil( + (async () => { + const cache = await caches.open(CACHE_NAME) + // 使用 {cache: 'reload'} 确保从网络获取最新的离线页面 + // 而不是从HTTP缓存中获取 + await cache.add(new Request(OFFLINE_URL, { cache: 'reload' })) + })(), + ) + // 强制等待中的Service Worker立即成为活动的Service Worker + self.skipWaiting() +}) + +// 激活事件 +self.addEventListener('activate', event => { + console.log('Service Worker activate') + event.waitUntil( + (async () => { + // 启用导航预载功能以提高性能 + if ('navigationPreload' in self.registration) { + await self.registration.navigationPreload.enable() + } + })(), + ) + // 告诉活动的Service Worker立即控制页面 + self.clients.claim() +}) + +// Fetch事件 - 处理网络请求和离线回退 +self.addEventListener('fetch', event => { + // 只处理HTML页面的导航请求 + if (event.request.mode === 'navigate') { + event.respondWith( + (async () => { + try { + // 首先尝试使用navigationPreload响应(如果支持) + const preloadResponse = await event.preloadResponse + if (preloadResponse) { + return preloadResponse + } + + // 总是优先尝试网络请求 + const networkResponse = await fetch(event.request) + return networkResponse + } catch (error) { + // 只有在抛出异常时才会触发catch,通常是由于网络错误 + // 如果fetch()返回4xx或5xx响应码,不会触发catch + console.log('网络请求失败,返回离线页面:', error) + + const cache = await caches.open(CACHE_NAME) + const cachedResponse = await cache.match(OFFLINE_URL) + return cachedResponse || new Response('离线页面不可用', { status: 503 }) + } + })(), + ) + } +}) + // 监听 push 事件,显示通知 self.addEventListener('push', function (event) { console.log('notification push') @@ -180,18 +206,6 @@ self.addEventListener('push', function (event) { } }) -// 安装 -self.addEventListener('install', function (e) { - console.log('worker install') - self.skipWaiting() -}) - -// 激活 -self.addEventListener('activate', function (e) { - console.log('worker activate') - e.waitUntil(self.clients.claim()) -}) - // 监听通知点击事件 self.addEventListener('notificationclick', function (event) { console.log('notification click')