Workbox Service Worker导致SPA路由失效问题求助
问题
通过InjectManifest模式配置Workbox(仅设置swSrc和swDest属性),并创建了对应的Service Worker(代码如下)。从站点根目录启动时一切正常,但从React Router管理的其他页面(比如刷新页面)启动时,Service Worker报错:Cannot get /page(其中page为尝试加载的URL路由)。
观察发现Service Worker中未配置这些路由的处理逻辑,但它似乎阻止了请求任意URL时加载index.html文件。尝试使用registerRoute(new NavigationRoute(createHandlerBoundToURL('/index.html'))),但出现index.html未预缓存的错误。不想缓存index.html,避免因始终提供旧缓存页面导致无法下载新Service Worker,请问还有什么解决办法?
import { CacheableResponsePlugin } from 'workbox-cacheable-response/CacheableResponsePlugin'; import { CacheFirst } from 'workbox-strategies/CacheFirst'; import { StaleWhileRevalidate } from 'workbox-strategies/StaleWhileRevalidate'; import { ExpirationPlugin } from 'workbox-expiration/ExpirationPlugin'; import { precacheAndRoute } from 'workbox-precaching/precacheAndRoute'; import { registerRoute } from 'workbox-routing/registerRoute'; import { setCatchHandler } from 'workbox-routing/setCatchHandler'; import { setDefaultHandler } from 'workbox-routing/setDefaultHandler'; import { clientsClaim } from 'workbox-core'; import { NetworkOnly } from 'workbox-strategies'; const imageFallback = `/media/catalog/product/p/l/placeholder.jpg`; precacheAndRoute(self.__WB_MANIFEST); clientsClaim(); setDefaultHandler(new NetworkOnly()); registerRoute( new RegExp('(robots.txt|favicon.ico|manifest.json)'), new StaleWhileRevalidate() ); // Handle images hosted with the PWA, that means they'll be served from the same origin registerRoute( ({ url, sameOrigin }) => { // Only cache images from the same origin if (!sameOrigin) return false; return url.pathname.match(/\.(?:png|gif|jpg|jpeg|svg|pjpg|webp)$/); }, new StaleWhileRevalidate({ cacheName: 'local-images', matchOptions: { ignoreVary: true }, plugins: [ new ExpirationPlugin({ // Keep at most 100 entries maxEntries: 100, // Don't keep any entries for more than 7 days maxAgeSeconds: 7 * 24 * 60 * 60, // Automatically cleanup if quota is exceeded purgeOnQuotaError: true }), new CacheableResponsePlugin({ statuses: [0, 200] }) ] }) ); // Handle images loaded from the BE URL. This likely means product images registerRoute( ({ url }) => { if (url.origin !== __MAGENTO_BE_URL__) return false; return url.pathname.match(/\.(?:png|gif|jpg|jpeg|svg|pjpg|webp)$/); }, new StaleWhileRevalidate({ cacheName: 'remote-images', matchOptions: { ignoreVary: true }, plugins: [ new ExpirationPlugin({ // Keep at most 100 entries maxEntries: 100, // Don't keep any entries for more than 7 days maxAgeSeconds: 7 * 24 * 60 * 60, // Automatically cleanup if quota is exceeded purgeOnQuotaError: true }), new CacheableResponsePlugin({ statuses: [0, 200] }) ] }) ); registerRoute(new RegExp(/\.js$/), new CacheFirst()); self.addEventListener('fetch', event => { const { request } = event; const responsePromise = router.handleRequest({ event, request }); if (responsePromise) { // Router found a route to handle the request. event.respondWith(responsePromise); } else { // No route was found to handle the request. // Fallback to network } }); const catchHandler = async options => { const dest = options.request.destination; const cache = await self.caches.open('workbox-offline-fallbacks'); if (dest === 'image' && imageFallback !== false) { return (await cache.match(imageFallback)) || Response.error(); } return Response.error(); }; setCatchHandler(catchHandler); self.addEventListener('install', event => { const files = []; if (imageFallback) { files.push(imageFallback); } event.waitUntil( self.caches .open('workbox-offline-fallbacks') .then(cache => cache.addAll(files)) ); }); self.addEventListener('message', event => { if (event.data && event.data.type === 'SKIP_WAITING') { self.skipWaiting(); } });
解决办法
核心思路是针对导航请求直接走网络请求/index.html,不缓存它,同时确保Workbox的路由能正确匹配这些单页路由。具体调整如下:
1. 补充导入导航路由模块
在代码开头的导入部分添加:
import { NavigationRoute } from 'workbox-routing/NavigationRoute';
2. 注册导航路由规则
在setDefaultHandler(new NetworkOnly());之后添加以下代码:
// 处理所有导航请求,直接走网络拉取最新的index.html registerRoute(new NavigationRoute(new NetworkOnly({ fetchOptions: { credentials: 'same-origin', // 保持同域凭证,按需调整 }, }), { // 排除不需要重定向到index.html的路径,比如API、静态资源 denylist: [/^\/api/, /\.(js|css|png|jpg|svg)$/], }));
3. 移除冲突的手动fetch监听
删除这段手动的fetch事件监听代码(Workbox路由系统已自动处理fetch逻辑,手动监听会引发冲突):
self.addEventListener('fetch', event => { const { request } = event; const responsePromise = router.handleRequest({ event, request }); if (responsePromise) { // Router found a route to handle the request. event.respondWith(responsePromise); } else { // No route was found to handle the request. // Fallback to network } });
调整说明
- 导航请求(比如刷新
/page)会被NavigationRoute捕获,直接发起网络请求获取最新的index.html,完全不缓存,确保每次都能拿到最新页面,也不会干扰Service Worker的更新流程 denylist参数可以过滤掉不需要重定向的路径,避免API接口、静态资源等被错误导向index.html- 移除手动fetch监听是因为Workbox内部已经封装了路由处理逻辑,重复监听会导致请求处理混乱,引发
Cannot get /page这类错误
内容的提问来源于stack exchange,提问作者Denno
相关产品推荐
相关产品推荐

