你以为开了 PWA 导航就快了?每次多等 50ms 不是代码的错

每次用户点链接,浏览器要干三件事:加载主文档、启动 ServiceWorker、在 SW 的 fetch 事件里决定怎么响应。

问题就出在”启动 ServiceWorker”这一步。

SW 启动不是即时的。Chrome DevTools 数据显示,一般设备约 50ms,冷启或低端机能到 250ms,极端情况超 500ms。这段时间里,浏览器是在等 SW 启动完,才发网络请求。用户看到的就是:点了链接→等 SW→才看到网络请求→拿到页面。

这就是为什么有时候开了 PWA,首页反而比不开慢。

以前怎么解?手动开 NavigationPreload

Jake Archibald 早在 2017 年就在 web.dev 写过 NavigationPreload:让浏览器在 SW 启动的同时发网络请求,两件事并行。

但这需要改两处代码:

// 1. 注册时开启
navigator.serviceWorker.register("/sw.js").then(r => {
  r.navigationPreload.enable();
});

// 2. SW 里消费 preloadResponse
addEventListener("fetch", event => {
  if (event.request.destination === "document") {
    event.respondWith(
      event.preloadResponse || fetch(event.request)
    );
  }
});

很多团队觉得这门槛高,宁可忍受这几百毫秒的阻塞。

Chrome 154 自动帮你做了这件事

Chrome 154 带来了 ServiceWorkerAutoPreload,本质上是自动版 NavigationPreload:浏览器在 SW 启动的同时自动发导航请求,不需要你改一行代码,SW 里也不需要处理 preloadResponse

效果和手动开 NavigationPreload 一样:SW 启动和网络请求并行,省掉等待时间。

从 WICG 的 explainer 来看,这个行为是默认开启的,Chrome 会在所有支持该协议的导航请求上自动并行。只有企业管理员通过 ServiceWorkerAutoPreloadEnabled 策略禁用它(设为 Disabled 时,浏览器会退回到等 SW 启动完才发请求的旧行为)。

实测数据:从 500ms 到 250ms

Jake Archibald 2017 年的演示里,故意在 SW 里加了 500ms 启动延迟:

  • 没 NavigationPreload:SW 启动 500ms → 才发网络请求 → 页面才来,总耗时约 700ms
  • 有 NavigationPreload:SW 启动 500ms 同时网络请求已在路上 → 总耗时降到约 400ms

实际生产环境没这么极端,但每次导航省 50-250ms,对 INP 和 LCP 的影响是实打实的。

适合谁?以及什么时候别用

适合:

  • SW 用 NetworkFirst 策略处理导航请求的 PWA
  • 没在预缓存 HTML 的 SPA(否则 preload 拉一遍又不用,白跑一次网络)

别用(会被拖慢):

  • 已用 Workbox 全面预缓存 HTML 的站
  • SW 逻辑极轻、启动本来就快(并行收益不大)

判断方法:下次打开 DevTools 的 Network 面板,清掉缓存硬刷新,看第一个 document 请求的 timing——如果有较长的「Waiting for ServiceWorker」时间,这个改动对你就有价值。

三步下一步

  1. 验证:Chrome 154+ 打开 DevTools → Network → 清缓存硬刷新 → 看 document 请求的「Starting Worker」时间有没有变短
  2. 确认 SW 逻辑:如果 SW 用 NetworkFirst 但没开 preload,手动加上 navigationPreload.enable() + event.preloadResponse,Chrome 154 会自动给这段再加一层保险
  3. 弱网实测:关 4G 限速到 Slow 3G,对比 Chrome 153 和 154 的首屏时间差

Chrome 154 正在 stepped rollout,稳定版预计 9 月 9 日全量推送。不用改代码,等着就是了。

评论区

0 条评论

登录后可评论。

阿速·性能优化 15 阅读