配了多年 Service Worker,今天才发现每次路由匹配都在等它冷启动——Chrome 123 把这件事彻底原生化了

每次 Service Worker 收到请求,都要先把它从磁盘唤醒才能执行 fetch 处理器——这一步在冷启动时通常是 50~500ms,你的 LCP 可能就卡在这上面。

这听起来像是个”忍着就好”的老大难问题。但从 Chrome 123 开始,浏览器其实给 Service Worker 提供了一个绕过去的出口:Static Routing API

它的原理很简单:在 install 事件里提前把路由规则注册好,之后浏览器收到匹配请求,直接在进程内处理,根本不触发 Service Worker 的 fetch 事件。

// 以前:所有请求都要走 fetch handler,包括永远从网络的
self.addEventListener("fetch", (event) => {
  if (event.request.url.includes("/api/")) {
    event.respondWith(fetch(event.request));
  }
  // ...其他规则
});

// 现在:install 时声明路由,浏览器直接路由,请求根本不进 SW
self.addEventListener("install", (event) => {
  event.addRoutes([
    {
      condition: { urlPattern: "/api/*" },
      source: "network", // 永远走网络,不触发 SW
    },
    {
      condition: { urlPattern: "/static/*" },
      source: { type: "cache", cacheName: "static-v1" }, // 直接从缓存读
    },
  ]);
});

这样一来,/api/* 的请求全程没有 Service Worker 参与,冷启动 50~500ms 的惩罚直接归零。Chrome 的测试数据显示,使用静态路由后,Lighthouse 的 TTFB 在冷加载场景下可以降低 200~400ms。

兼容性与渐进增强

这个 API 的渐进增强设计得很好:只有 Chrome/Edge 123+ 认识 addRoutes,其他浏览器会直接跳过定义,跳到原有的 fetch handler——你的降级方案本身就是兜底逻辑,不需要额外代码。

self.addEventListener("install", (event) => {
  if (!("addRoutes" in event)) return; // 旧浏览器直接跳过

  event.addRoutes([ /* 规则 */ ]);
});

和 Safari 的 addRoutes 是什么关系?

Safari 27 也加了 addRoutes,但它是在 install event 上加的路由能力,语义相同,实现独立。两者的 condition 语法基本一致,都是 urlPattern + requestMethod + runningStatus 那几个字段。

下一步怎么落地

如果你现在用 Workbox,它的 registerRoute 已经在内部编译成 addRoutes 调用了,直接升级 Workbox 就能享受这个优化。如果是自己手写的 SW,找一找 fetch handler 顶部那些「只按 URL 判断、永远走网络」的 if-else 规则,把它们迁移到 install 的 addRoutes 里——这是收益最大的场景。

Chrome 从四周一版切换到两周一版,这个优化会变得越来越值得标配:因为 Service Worker 的启动延迟在每次版本切换的测试窗口里都是一个容易被忽略的性能黑洞。

评论区

0 条评论

登录后可评论。

阿速·性能优化 14 阅读