PWA离线访问Workbox source maps报ERR_INTERNET_DISCONNECTED错误
Workbox Service Worker 实际离线触发恐龙页、DevTools离线模式正常问题修复
问题背景
我有一个基于Workbox 6.5.3开发的Service Worker,预期实现三个核心功能:
- 预缓存指定URL列表资源
- 采用StaleWhileRevalidate策略做缓存更新
- 通过后台同步队列处理离线状态下的POST请求
对应Service Worker代码如下:
importScripts("/third_party/workbox/workbox-v6.5.3/workbox-sw.js"); workbox.setConfig({ debug: true, modulePathPrefix: "/third_party/workbox/workbox-v6.5.3/", }); const { registerRoute, Router } = workbox.routing; const { StaleWhileRevalidate } = workbox.strategies; const { CacheableResponsePlugin } = workbox.cacheableResponse; const { RangeRequestsPlugin } = workbox.rangeRequests; const router = new Router(); const urls = [/*URLs to be cached including all files in the workbox folder*/]; const exclude = ({ url, request, event }) => { let exclude = url.pathname.match(/tools\/horario/) || url.pathname.match(/tools\/modal_abierto/); let include = !exclude; return include; }; self.addEventListener("install", function (event) { const strategy = new StaleWhileRevalidate({ cacheName: "demo-cache", plugins: [ new CacheableResponsePlugin({ statuses: [0, 200], }), new RangeRequestsPlugin(), ], }); registerRoute(exclude, strategy); event.waitUntil( caches.open("demo-cache").then(function (cache) { return cache.addAll(urls); }) ); const { Queue } = workbox.backgroundSync; const queue = new Queue("myQueueName"); POSThandler = ({ url, request, event, params }) => { const bgSyncLogic = async () => { try { const response = await fetch(event.request.clone()); return response; } catch (error) { await queue.pushRequest({ request: event.request }); return error; } }; event.respondWith(bgSyncLogic()); }; POSTinclude = () => { return true; }; registerRoute(POSTinclude, POSThandler, "POST"); workbox.routing.setDefaultHandler(strategy); }); 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. } });
异常表现
在DevTools中勾选离线模式重载页面时,代码可正常运行、加载完整页面;但设备实际处于离线状态时,页面会直接跳转到Chrome内置的恐龙离线报错页,控制台输出Workbox source maps相关ERR_INTERNET_DISCONNECTED错误。
问题原因&修复方案
现有配置存在3个核心错误,按影响优先级排序:
- 路由注册位置错误、自定义fetch逻辑与内置逻辑冲突
Workbox的registerRoute、setDefaultHandler逻辑必须写在Service Worker的顶层作用域,不能放在install事件回调里。install事件仅在Service Worker首次安装时触发一次,且你手动new了独立的Router实例、自己编写了fetch事件监听,而registerRoute默认把规则注册到Workbox内置的Router实例上,两个Router完全不互通,绝大多数场景下路由规则根本不会生效,请求没有被respondWith处理,浏览器就会直接发起网络请求,离线时自然跳恐龙页。
DevTools离线模式是模拟网络断开,不会触发真实离线时的Service Worker请求校验逻辑,因此模拟环境下表现正常。 - debug模式依赖的source map资源未缓存
开启debug: true后,Workbox会自动请求对应的source map文件做调试映射,实际离线时这些map文件没有加入预缓存列表,请求直接失败抛出ERR_INTERNET_DISCONNECTED,浏览器会判定Service Worker未正确处理请求,直接兜底跳恐龙页。 - POST请求离线兜底逻辑非法
离线时把请求加入队列后直接返回error对象,没有返回合法的Response实例,浏览器会将该情况判定为请求处理失败。
修复后完整代码
importScripts("/third_party/workbox/workbox-v6.5.3/workbox-sw.js"); // 生产环境将debug设为false,不会自动请求source map workbox.setConfig({ debug: false, modulePathPrefix: "/third_party/workbox/workbox-v6.5.3/", }); const { registerRoute } = workbox.routing; const { StaleWhileRevalidate } = workbox.strategies; const { CacheableResponsePlugin } = workbox.cacheableResponse; const { RangeRequestsPlugin } = workbox.rangeRequests; const { Queue } = workbox.backgroundSync; // 所有路由、策略逻辑放在顶层作用域,不要写入install事件 const strategy = new StaleWhileRevalidate({ cacheName: "demo-cache", plugins: [ new CacheableResponsePlugin({ statuses: [0, 200], }), new RangeRequestsPlugin(), ], }); // 路由匹配规则 const matchIncludePath = ({ url }) => { return !(url.pathname.match(/tools\/horario/) || url.pathname.match(/tools\/modal_abierto/)); }; registerRoute(matchIncludePath, strategy); // 预缓存逻辑可保留在install事件中 const urls = [/* 填入所有需要预缓存的资源,包括站点入口HTML、Workbox目录下所有静态文件,若开启debug需把对应.map文件也加入列表 */]; self.addEventListener("install", (event) => { event.waitUntil( caches.open("demo-cache").then((cache) => cache.addAll(urls)) ); self.skipWaiting(); }); self.addEventListener("activate", (event) => { event.waitUntil(clients.claim()); }); // 后台同步POST队列 const queue = new Queue("myQueueName"); const POSTHandler = async ({ event }) => { try { return await fetch(event.request.clone()); } catch (error) { await queue.pushRequest({ request: event.request }); // 离线场景必须返回合法Response对象 return new Response(JSON.stringify({ code: 0, msg: "当前处于离线状态,请求已加入队列,网络恢复后将自动提交" }), { headers: { "Content-Type": "application/json" } }); } }; registerRoute( () => true, POSTHandler, "POST" ); // 配置默认处理器,删除手动创建的Router实例和自定义fetch监听,Workbox会自动接管fetch事件 workbox.routing.setDefaultHandler(strategy);
额外验证点
- 若需保留debug模式,必须将Workbox对应版本的所有
.map文件加入预缓存列表,否则离线时map请求失败仍会触发错误 - 预缓存列表必须包含站点导航对应的HTML入口文件,否则离线打开首页时无缓存资源会直接加载失败
- 不要手动重写fetch事件调用自定义Router实例,避免和Workbox内置的fetch监听逻辑冲突,导致请求未被正确处理
内容的提问来源于stack exchange,提问作者Asiesjhot Singh Chandowk
相关产品推荐
相关产品推荐

