Navigation Preload 是浏览器在 Service Worker 启动的同时并行发起原始网络请求,解决 SW 初始化与首屏资源加载的串行阻塞;需注册时调用 enable() 显式开启,fetch 中对 document 请求安全链式 fallback:preloadResponse → cache → fetch。

Navigation Preload 不是让 Service Worker “提前启动”,而是让浏览器在 SW 启动的同时,并行发起原始网络请求。它解决的是“SW 初始化耗时”与“等待首屏资源”之间的串行阻塞问题,真正实现首屏加载不卡顿。
为什么需要 Navigation Preload
用户点击链接或输入 URL 触发导航时,浏览器必须:先唤醒 Service Worker(可能需解压、解析、执行 JS),再让它处理 fetch 事件。这个过程通常要几十毫秒——对首屏 HTML 这种关键请求来说,就是明显延迟。Navigation Preload 把这一步“并行化”:浏览器在 dispatch install/activate 的同时,就悄悄发出了原始请求,结果准备好后直接交给 fetch 事件使用。
启用 Navigation Preload 的正确姿势
必须在注册阶段显式开启,且仅对 navigation 类型请求生效:
- 注册代码中调用 navigationPreload.enable(),不能只写
if (registration.navigationPreload)就结束,要确保 Promise 成功 resolve - 检查是否支持:
if ('navigationPreload' in registration),不支持的旧版浏览器会跳过 - 推荐写法(带错误兜底):
registration.navigationPreload.enable().catch(() => {});
在 fetch 中安全使用 preloadResponse
不是所有请求都有 preloadResponse,也不是所有 preloadResponse 都能直接返回。需注意三点:
- 只对 destination === 'document' 的请求启用,避免误用于图片、API 等非导航请求
- preloadResponse 是一个 Promise,可能 reject(如网络失败、CORS 拒绝),必须用 .catch(() => null) 容错
- 不要直接
return event.preloadResponse,应链式 fallback 到缓存或 fetch:
event.preloadResponse.then(r => r || caches.match(event.request))
配合预缓存才能发挥最大效果
Navigation Preload 解决“新请求不等 SW”,但无法替代静态资源的本地秒回。必须和 install 阶段的 precache 协同:
- 预缓存首页骨架文件(/index.html、/app.css、/runtime.js),HTML 必须是构建时生成的纯静态版本
- 避免在 install 中用 fetch() 手动拉首页——权限未就绪、流未 clone、动态内容污染缓存,三重风险
- 最终响应逻辑是三层 fallback:preloadResponse → cache → fetch,顺序不可颠倒










