navigation preload 通过并行发起网络请求打破 service worker 冷启动阻塞,需注册时启用、fetch 中消费 preloadresponse 并用 waituntil 保生命周期,配合预缓存实现秒开。

Service Worker 启动本身有延迟,尤其冷启动时,浏览器会等它 ready 才处理导航请求——这直接卡住首屏。Navigation Preload 的作用,就是打破这个等待链:让浏览器在 SW 初始化的同时,并行发起网络请求,把“等 SW”和“拉资源”两个动作重叠执行。
为什么默认会阻塞?
用户点击链接或刷新页面时,浏览器需完成三件事:加载主文档、激活已注册的 Service Worker、再由 SW 的 fetch 事件决定如何响应。但旧版逻辑是串行的——必须等 SW 的 install/activate 完成,才发网络请求。如果 SW 启动慢(比如含大量初始化代码或缓存逻辑),首屏就白屏卡住。
启用 Navigation Preload 的关键步骤
它不是开个开关就行,需要注册端和 SW 内部协同:
- 注册时立刻启用:
navigator.serviceWorker.register('/sw.js').then(r => r.navigationPreload.enable());注意加判断if (r.navigationPreload),避免低版本报错 - SW 脚本中,在
fetch事件里主动消费event.preloadResponse,而不是只依赖caches.match() - 必须用
event.waitUntil()包裹异步逻辑,否则 SW 可能在 preloadResponse 返回前就退出,触发 “request was cancelled” 错误
fetch 事件中的正确响应模式
对 document 类型请求(即页面导航),应优先信任预加载响应,它最快、最原始:
- 检查
event.request.destination === 'document'或 URL 是否为根路径 - 用
event.preloadResponse获取浏览器并发拉回的响应;若存在,直接返回,不查缓存也不重发 fetch - 若
preloadResponse为空或失败,再 fallback 到缓存匹配;缓存也无则兜底 fetch - 所有异步分支都通过
event.waitUntil(Promise.all([...]))显式延长生命周期,确保不被提前终止
配合预缓存才能真正“秒开”
Navigation Preload 解决的是“新 SW 版本首次运行时的卡顿”,但它不解决“用户第一次访问没缓存”的问题。所以必须搭配 install 阶段的 precache:
- 在
install事件中用caches.open('v1-static').addAll([...])预存骨架 HTML、核心 CSS、基础 JS - 这些文件必须是构建时生成的纯静态版本,不能含服务端动态插入内容(如时间戳、用户昵称)
- 预缓存 + Navigation Preload 是双轨:一个保“已有资源极速返回”,一个保“SW 启动时不空等”










