正在投放

渐进式 Web 应用的一个重要方面是可靠;它们可以快速加载资源,从而保持用户的活跃度并立即提供反馈,即使在网络条件较差的情况下也可以。为什么会这样?这要归功于 Service Worker fetch 事件。

提取事件

浏览器支持

  • Chrome:40。 <ph type="x-smartling-placeholder">
  • Edge:17。 <ph type="x-smartling-placeholder">
  • Firefox:44。 <ph type="x-smartling-placeholder">
  • Safari:11.1. <ph type="x-smartling-placeholder">

来源

通过 fetch 事件,我们可以拦截 PWA 在 Service Worker 作用域内针对同源和跨源请求发出的每个网络请求。除了导航和资源请求之外,通过从已安装的 Service Worker 获取数据,无需网络调用即可呈现网站首次加载后的网页访问。

fetch 处理程序会从应用接收所有请求(包括网址和 HTTP 标头),并允许应用开发者决定如何处理这些请求。

Service Worker 位于客户端和网络之间。

您的 Service Worker 可以将请求转发到网络,使用先前缓存的响应进行响应,或创建新的响应。一切由你决定。 下面是一个简单的示例:

self.addEventListener("fetch", event => {
    console.log(`URL requested: ${event.request.url}`);
});

响应请求

当请求到达 Service Worker 时,您可以做两件事:您可以忽略它,让它进入网络;您也可以直接回应它。从 Service Worker 中响应请求是您选择内容的方式,以及如何将其返回给 PWA,即使在用户离线时也是如此。

如需响应传入请求,请从 fetch 事件处理脚本中调用 event.respondWith(),如下所示:

// fetch event handler in your service worker file
self.addEventListener("fetch", event => {
    const response = .... // a response or a Promise of response
    event.respondWith(response);
});

您必须同步调用 respondWith(),并且必须返回 Response 对象。但是,在提取事件处理脚本完成后(例如在异步调用中),您无法调用 respondWith()。如果您需要等待完整响应,可以将 Promise 传递给使用 Response 进行解析的 respondWith()

创建响应

借助 Fetch API,您可以在 JavaScript 代码中创建 HTTP 响应,并且这些响应可以使用 Cache Storage API 进行缓存并返回,就像它们来自网络服务器一样。

如需创建响应,请创建一个新的 Response 对象,并设置其正文和状态和标头等选项:

const simpleResponse = new Response("Body of the HTTP response");

const options = {
   status: 200,
   headers: {
    'Content-type': 'text/html'
   }
};
const htmlResponse = new Response("<b>HTML</b> content", options)

从缓存响应

现在,您已了解如何从 Service Worker 发出 HTTP 响应, 可以使用缓存存储接口在设备上存储资源。

您可以使用 Cache Storage API 检查从 PWA 收到的请求是否位于缓存中,如果有,则使用它响应 respondWith()。 为此,您首先需要在缓存中进行搜索。顶级 caches 接口中的 match() 函数可搜索您来源中的所有商店,或针对单个打开的缓存对象。

match() 函数接收 HTTP 请求或网址作为参数,并返回一个使用与相应键关联的响应进行解析的 promise。

// Global search on all caches in the current origin
caches.match(urlOrRequest).then(response => {
   console.log(response ? response : "It's not in the cache");
});

// Cache-specific search
caches.open("pwa-assets").then(cache => {
  cache.match(urlOrRequest).then(response => {
    console.log(response ? response : "It's not in the cache");
  });
});

缓存策略

仅从浏览器缓存传送文件并不适用于所有用例。例如,用户或浏览器可以逐出缓存。因此,您应为 PWA 定义自己的资产分发策略。 您并非只能使用一种缓存策略。您可以为不同的网址格式定义不同的网址。例如,您可以为最少量界面资源设置一种策略,为 API 调用使用另一种策略,为图片和数据网址使用第三种策略。 为此,请阅读 ServiceWorkerGlobalScope.onfetch 中的 event.request.url,并通过正则表达式或网址格式对其进行解析。(在撰写本文时,并非所有平台都支持网址格式。)

最常用的策略包括:

优先缓存
首先搜索缓存的响应,如果找不到缓存的响应,则回退到网络。
广告联盟优先
首先从网络请求响应,如果未返回任何响应,则检查缓存中的响应。
重新验证时过时
从缓存传送响应,同时在后台请求最新版本,并将其保存到缓存中,以便下次请求资源时使用。
仅限网络
始终在回复时提供网络响应或错误提示。绝不会查询缓存。
仅缓存
始终使用缓存中的响应进行回复或返回错误。绝不会咨询该网络。使用此策略提供的资产必须在请求前添加到缓存中。

优先缓存

使用此策略时,Service Worker 会在缓存中查找匹配的请求,并在缓存缓存后返回相应的响应。否则,它会从网络检索响应(可以选择更新缓存以供将来的调用使用)。如果既没有缓存响应,也没有网络响应,则请求将出错。由于不向网络投放素材资源的速度往往会更快,因此该策略优先考虑性能而非时效性。

缓存优先策略

self.addEventListener("fetch", event => {
   event.respondWith(
     caches.match(event.request)
     .then(cachedResponse => {
       // It can update the cache to serve updated content on the next request
         return cachedResponse || fetch(event.request);
     }
   )
  )
});

广告联盟优先

此策略是“缓存优先”策略的镜像;它会检查是否可以从网络处理请求,如果不能处理,则尝试从缓存中检索请求。比如缓存优先。如果既没有网络响应也没有缓存响应,则请求将出错。从网络获取响应通常比从缓存获取响应更慢,此策略优先考虑更新的内容而非性能。

“网络优先”策略

self.addEventListener("fetch", event => {
   event.respondWith(
     fetch(event.request)
     .catch(error => {
       return caches.match(event.request) ;
     })
   );
});

重新验证时过时

“过时时重新验证”策略会立即返回一个缓存的响应,然后检查网络是否有更新,如果找到缓存的响应,则替换该响应。此策略始终会发出网络请求,因为即使找到缓存的资源,也会尝试使用从网络收到的内容更新缓存中的内容,以便在下一个请求中使用更新后的版本。因此,此策略提供了一种方法,让您从缓存优先策略的快速提供中受益并在后台更新缓存。

过时的重新验证策略

self.addEventListener('fetch', event => {
  event.respondWith(
    caches.match(event.request).then(cachedResponse => {
        const networkFetch = fetch(event.request).then(response => {
          // update the cache with a clone of the network response
          const responseClone = response.clone()
          caches.open(url.searchParams.get('name')).then(cache => {
            cache.put(event.request, responseClone)
          })
          return response
        }).catch(function (reason) {
          console.error('ServiceWorker fetch failed: ', reason)
        })
        // prioritize cached response over network
        return cachedResponse || networkFetch
      }
    )
  )
})

仅限网络

“仅限网络”策略与浏览器在没有 Service Worker 或 Cache Storage API 时的行为方式类似。请求将仅返回可从网络中提取的资源。这对于诸如仅在线的 API 请求之类的资源通常很有用。

“仅限广告网络”策略

仅缓存

“仅缓存”策略可确保请求永远不会进入网络;系统会使用预先填充的缓存项响应所有传入请求。以下代码将 fetch 事件处理脚本与缓存存储空间的 match 方法结合使用,以仅响应缓存:

self.addEventListener("fetch", event => {
   event.respondWith(caches.match(event.request));
});

仅缓存策略。

自定义策略

以上是常见的缓存策略,但您要负责 Service Worker 以及请求的处理方式。如果这些方法都无法满足您的需求,您可以自行创建策略。

例如,您可以采用“网络优先”策略并设有超时,以优先更新更新内容,但前提是响应的出现次数不超过您设置的阈值。您还可以将缓存的响应与网络响应合并,并通过 Service Worker 构建复杂的响应。

更新资产

使 PWA 的缓存资源保持最新可能是一项挑战。虽然“过时的重新验证”策略是实现这一目的的一种方式,但并不是唯一的。在“更新”这一章中,您将了解更新应用内容和资产的不同技巧。

资源