NavigationPreloadManager

Baseline 已广泛支持

此功能已经非常成熟,可在多种设备和浏览器版本上使用。自 2022 年 4 月以来,它已在各大浏览器中得到支持。

安全上下文: 此功能仅在安全上下文(HTTPS)中可用,且支持此功能的浏览器数量有限。

注意:此功能在 Web Workers 中可用。

NavigationPreloadManager 接口是 Service Worker API 的一部分,它提供了用于在 Service Worker 启动期间并行管理资源预加载的方法。

如果受支持,该类型的对象将通过 ServiceWorkerRegistration.navigationPreload 返回。预加载 fetch 请求的结果可以通过 FetchEvent.preloadResponse 返回的 Promise 来等待。

实例方法

启用导航预加载,返回一个解析为 undefinedPromise

禁用导航预加载,返回一个解析为 undefinedPromise

设置在预加载请求中发送的 Service-Worker-Navigation-Preload HTTP 头的的值,并返回一个空的 Promise

返回一个 Promise,该 Promise 解析为一个对象,其中包含指示预加载是否已启用以及在预加载请求中 Service-Worker-Navigation-Preload HTTP 头中将发送什么值的属性。

描述

Service Worker 代表网站处理特定作用域内的页面 fetch() 事件。当用户导航到一个使用 Service Worker 的页面时,浏览器会启动该 Service Worker(如果它尚未运行),然后向其发送一个 fetch 事件并等待结果。收到事件后,Worker 会在缓存中查找资源,如果找到则返回,否则从远程服务器获取资源(并存储一份以备将来请求使用)。

Service Worker 在启动之前无法处理来自浏览器的事件。这是不可避免的,但通常影响不大。Service Worker 通常已经启动(在处理其他请求后会保持活动一段时间)。即使 Service Worker 需要启动,很多时候它也会从缓存返回数据,这非常快。然而,在那些 Worker 需要在开始从远程服务器获取资源之前启动的情况下,延迟可能会很显著。

NavigationPreloadManager 提供了一种机制,允许在 Service Worker 启动的同时获取资源,这样,当 Worker 能够处理来自浏览器的 fetch 请求时,资源可能已经完全或部分下载。这使得 Worker 启动的情况“不比”Worker 已经启动时更糟,有时甚至更好。

预加载管理器会在预加载请求中发送 Service-Worker-Navigation-Preload HTTP 头,允许响应针对预加载请求进行自定义。例如,这可以用来减少发送到页面的数据量,仅限于原始页面的部分内容,或者根据用户的登录状态自定义响应。

示例

这里的示例来自 使用导航预加载加速 Service Worker (developer.chrome.com)。

功能检测和启用导航预加载

在下面的示例中,我们在 Service Worker 的 activate 事件处理程序中启用导航预加载。在此之前,我们首先使用 ServiceWorkerRegistration.navigationPreload 来确定该功能是否受支持(如果支持,它会返回 Service Worker 的 NavigationPreloadManager;如果不支持,则返回 undefined)。

js
addEventListener("activate", (event) => {
  event.waitUntil(
    (async () => {
      if (self.registration.navigationPreload) {
        // Enable navigation preloads!
        await self.registration.navigationPreload.enable();
      }
    })(),
  );
});

使用预加载的响应

以下代码展示了一个 Service Worker 的 fetch 事件处理程序,该程序使用预加载的响应(FetchEvent.preloadResponse)。

fetch 事件处理程序调用 FetchEvent.respondWith(),将一个 Promise 返回给受控页面。这个 Promise 将会用请求的资源来解析,该资源可能来自缓存、预加载的 fetch 请求或新的网络请求。

如果 Cache 对象中存在匹配的 URL 请求,则代码返回一个用于从缓存获取响应的已解析 Promise。如果缓存中未找到匹配项,代码将返回已解析的预加载响应(FetchEvent.preloadResponse)。如果缓存条目或预加载响应均未匹配,代码将启动一个新的网络 fetch 操作,并返回该 fetch 操作的(未解析的)Promise。

js
addEventListener("fetch", (event) => {
  event.respondWith(
    (async () => {
      // Respond from the cache if we can
      const cachedResponse = await caches.match(event.request);
      if (cachedResponse) return cachedResponse;

      // Else, use the preloaded response, if it's there
      const response = await event.preloadResponse;
      if (response) return response;

      // Else try the network.
      return fetch(event.request);
    })(),
  );
});

自定义响应

浏览器在预加载请求时会发送 HTTP 头 Service-Worker-Navigation-Preload,其默认指令值为 true。这允许服务器区分正常 fetch 请求和预加载 fetch 请求,并在必要时发送不同的响应。

注意:如果预加载和正常 fetch 操作的响应可能不同,那么服务器必须设置 Vary: Service-Worker-Navigation-Preload 来确保不同响应被正确缓存。

可以使用 NavigationPreloadManager.setHeaderValue() 将头的值更改为任何其他字符串值,以便为预取操作提供额外的上下文。例如,您可以将值设置为您最近缓存的资源的 ID,这样服务器就不会返回任何不需要的资源。同样,您也可以根据身份验证状态配置返回的信息,而不是使用 cookies。

以下代码演示了如何将头指令的值设置为变量 newValue

js
navigator.serviceWorker.ready
  .then((registration) =>
    registration.navigationPreload.setHeaderValue(newValue),
  )
  .then(() => {
    console.log("Done!");
  });

使用导航预加载加速 Service Worker > 预加载的自定义响应 提供了更完整的示例,其中一个文章网页的响应由缓存的头部和尾部构建,因此在预取时仅返回文章内容。

获取状态

您可以使用 NavigationPreloadManager.getState() 来检查导航预加载是否已启用,并确定在预加载请求中 Service-Worker-Navigation-Preload HTTP 头将发送什么指令值。

以下代码展示了如何获取解析为 state 对象的 Promise 并记录结果。

js
navigator.serviceWorker.ready
  .then((registration) => registration.navigationPreload.getState())
  .then((state) => {
    console.log(state.enabled); // boolean
    console.log(state.headerValue); // string
  });

规范

规范
Service Workers
# navigation-preload-manager

浏览器兼容性

另见