Navigation preload:让导航请求不必等待 service worker 启动
一句话: Navigation preload 让浏览器在 service worker 启动的同时并行开始获取导航请求,这样即使 worker 需要冷启动,也不会比已在运行的 worker 更慢。
它解决的问题
Section titled “它解决的问题”当用户导航到一个受 service worker 控制的页面时,浏览器必须启动 service worker(如果尚未运行)、
向它发送一个 fetch 事件,然后等待结果。service worker 在启动完成之前无法处理事件,而当 worker
确实需要从头启动时,这段启动延迟可能会拖慢导航请求的响应——即便最终的响应本可以来自通常非常快
的缓存。
Navigation preload 通过 NavigationPreloadManager.enable() 启用,通常放在 service worker 的
activate 事件处理器中,并通过 ServiceWorkerRegistration.navigationPreload 做特性检测:
addEventListener("activate", (event) => { event.waitUntil( (async () => { if (self.registration.navigationPreload) { await self.registration.navigationPreload.enable(); } })(), );});如果支持该特性,self.registration.navigationPreload 会返回 NavigationPreloadManager 对象,
否则返回 undefined。
| 方法 | 作用 |
|---|---|
enable() |
启用 navigation preload;返回一个 resolve 为 undefined 的 Promise。 |
disable() |
禁用 navigation preload;返回一个 resolve 为 undefined 的 Promise。 |
setHeaderValue() |
设置随预加载请求发送的 Service-Worker-Navigation-Preload HTTP 请求头的值;返回一个空的 Promise。 |
getState() |
返回一个 Promise,resolve 为一个对象,指示预加载是否启用以及使用的请求头值。 |
使用预加载的响应
Section titled “使用预加载的响应”Navigation preload 必须与 fetch 事件处理器配合使用:预加载的结果通过 FetchEvent.preloadResponse
读取,这是一个会 resolve 为 Response(若该请求未触发预加载则为 undefined)的 Promise:
addEventListener("fetch", (event) => { event.respondWith( (async () => { const cachedResponse = await caches.match(event.request); if (cachedResponse) return cachedResponse;
const response = await event.preloadResponse; if (response) return response;
return fetch(event.request); })(), );});只有当该 service worker 已启用 navigation preload、请求是 GET 请求、且请求是导航请求时,
preloadResponse 才会 resolve 为一个 Response;否则它会 resolve 为 undefined。该属性仅在
service worker 内部可用。
Service-Worker-Navigation-Preload 请求头
Section titled “Service-Worker-Navigation-Preload 请求头”浏览器会自动在预加载请求中带上 Service-Worker-Navigation-Preload 请求头(默认值为 true),
让服务器可以区分预加载请求与普通请求。setHeaderValue() 可以修改这个值,例如设置为一个缓存版本
ID:
navigator.serviceWorker.ready .then((registration) => registration.navigationPreload.setHeaderValue(newValue), ) .then(() => { console.log("Done!"); });如果普通响应与预加载响应可能不同,服务器必须设置 Vary: Service-Worker-Navigation-Preload,
才能让缓存行为保持正确。
NavigationPreloadManager 与 FetchEvent.preloadResponse 需要安全上下文(HTTPS),并且自
2022 年 4 月起属于 Baseline 广泛可用——已在许多设备和浏览器版本中得到充分确立的支持。
- 在
activate处理器中启用 navigation preload,并用特性检测加以保护。 - 在
fetch处理器中 awaitevent.preloadResponse;不要在其他地方使用该响应。 - 如果最终没有使用预加载请求的响应(例如命中了缓存),用
event.waitUntil()让该请求保持存活。 - 如果普通响应与预加载响应可能不同,在服务器上设置
Vary: Service-Worker-Navigation-Preload。
- Service Worker 生命周期 — 启用 navigation preload 的
activate事件 - Fetch 事件处理 — 读取
preloadResponse的fetch事件