You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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个核心错误,按影响优先级排序:

  1. 路由注册位置错误、自定义fetch逻辑与内置逻辑冲突
    Workbox的registerRoute、setDefaultHandler逻辑必须写在Service Worker的顶层作用域,不能放在install事件回调里。install事件仅在Service Worker首次安装时触发一次,且你手动new了独立的Router实例、自己编写了fetch事件监听,而registerRoute默认把规则注册到Workbox内置的Router实例上,两个Router完全不互通,绝大多数场景下路由规则根本不会生效,请求没有被respondWith处理,浏览器就会直接发起网络请求,离线时自然跳恐龙页。
    DevTools离线模式是模拟网络断开,不会触发真实离线时的Service Worker请求校验逻辑,因此模拟环境下表现正常。
  2. debug模式依赖的source map资源未缓存
    开启debug: true后,Workbox会自动请求对应的source map文件做调试映射,实际离线时这些map文件没有加入预缓存列表,请求直接失败抛出ERR_INTERNET_DISCONNECTED,浏览器会判定Service Worker未正确处理请求,直接兜底跳恐龙页。
  3. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 20:48:19