配了三年 PWA,每次首屏都要白屏等 ServiceWorker 启动——今天 Chrome 154 把这件事自动修了
做过 PWA 的都有这个经历:用户点了链接,页面得等上好一阵才开始加载——不是因为网络慢,是因为浏览器正在启动你的 ServiceWorker。
这个延迟不是小问题。ServiceWorker 冷启动时间:桌面端约 50ms,移动端接近 250ms,极端情况下超过 500ms。用户从新标签页或者其他网站点过来,每次都要经历这个冷启动等待。更要命的是,这个等待是串行的——必须等 ServiceWorker 完全启动,浏览器才发出网络请求。
这不是新问题。NavigationPreload 存在很多年了,它的核心思路是在 ServiceWorker 启动的同时浏览器就发出网络请求,然后在 fetch handler 里用 event.preloadResponse 消费掉。但问题在于,这需要你在注册时调用 navigationPreload.enable(),还要在每个导航请求的 fetch handler 里判断并使用 preloadResponse。国内大多数 PWA 项目根本不会配这一套。
Chrome 154 引入了一个改变这个局面的机制。浏览器会在 ServiceWorker 冷启动时自动并行发出网络请求,如果你的 fetch handler 最终走了 respondWith(fetch(event.request)),就消费掉这个预加载响应;如果你的 fetch handler 决定不拦截,auto-preload 的响应会直接作为后备被浏览器使用。最关键的是:完全不需要改任何代码。
它是怎么工作的
这个新行为(ServiceWorkerAutoPreload)核心在于 fetch handler 的决策过程:
场景 1:直通过网络
self.addEventListener("fetch", (event) => {
// 这种情况,浏览器会消费 auto-preload 的响应
event.respondWith(fetch(event.request));
});
场景 2:走缓存或返回自定义内容
self.addEventListener("fetch", (event) => {
event.respondWith(new Response("Custom response"));
// auto-preload 响应被丢弃,不影响
});
Chrome 团队的 WICG 提案里明确指出:即使 fetch handler 对请求做了拦截和修改,行为仍然是完全向后兼容的。如果 handler 没有调用 respondWith(),auto-preload 的响应会作为 fallback 被浏览器使用,不会触发二次请求。
这个机制是 Chrome 自动判断的——只有当你的 ServiceWorker 大部分时间直通知网络请求(pass-through),Chrome 才会开启这个优化。fetch handler 本身的行为逻辑完全不变,只是浏览器多了一个并行优化的选项。
你现在能做什么
Chrome 154 会在 9 月 9 日正式稳定发布,届时大多数 PWA 会自动获得这个优化。如果你需要主动退出,可以通过 Static Routing API 注册一个全匹配路由强制请求走 fetch handler 来实现。Chrome 团队表示未来可能还会加入企业级策略控制。
对于已经在使用 NavigationPreload 的项目,这个新行为是完全透明的——你的代码无需任何改动。
对于性能监控:Navigation Timing 里的 workerStart 延迟会下降,因为网络请求和 SW 启动现在是并行的而不是串行的。服务器端会看到 ServiceWorker-Auto-Preload: true 这个请求头,这是正常的,CDN 和网关都能正确处理。
Chrome 154 发布后,大多数使用 ServiceWorker 的站点会直接受益,首屏加载时间会有明显改善。还没配 NavigationPreload 的 PWA 项目会是最大的受益者——Chrome 帮你把这件事自动修了。
评论区
登录后可评论。