缓存

缓存存储是一项强大的工具。这可减少应用对网络状况的依赖。通过合理使用缓存,您可以让 Web 应用在离线状态下可用,并在任何网络条件下尽可能快速地提供素材资源。如素材资源和数据中所述,您可以决定缓存必要素材资源的最佳策略。如需管理您的服务工作线程与之交互的缓存,请使用 Cache Storage API

Browser Support

  • Chrome: 43.
  • Edge: 16.
  • Firefox: 41.
  • Safari: 11.1.

Source

Cache Storage API 可在不同上下文中使用:

  • 窗口上下文(PWA 的主线程)。
  • 服务工作线程。
  • 您使用的任何其他工作器。

使用 Service Worker 管理缓存的优势之一是,其生命周期不受窗口的限制,这意味着您不会阻塞主线程。请注意,如需使用 Cache Storage API,大多数此类上下文都必须通过 TLS 连接进行。

要缓存的内容

您可能首先会问,缓存什么内容?虽然这个问题没有唯一的答案,但您可以先从渲染界面所需的所有最低限度的资源开始。

这些资源应包括:

  • 主页面 HTML(应用的 start_url)。
  • 主界面所需的 CSS 样式表。
  • 界面中使用的图片。
  • 渲染界面所需的 JavaScript 文件。
  • 呈现基本体验所需的数据,例如 JSON 文件。
  • 网络字体。
  • 多网页应用中,您希望快速提供或在离线时提供的其他 HTML 文档。

可离线使用

虽然能够离线使用是渐进式 Web 应用的要求之一,但务必要了解,并非每个 PWA 都需要提供完整的离线体验,例如云游戏解决方案或加密资产应用。因此,提供一个基本界面来引导用户完成这些情况是可行的。

您的 PWA 不应呈现浏览器错误消息,指出 Web 呈现引擎无法加载网页。而是使用您的服务工作线程来显示您自己的消息,避免出现通用且令人困惑的浏览器错误。

您可以根据 PWA 的需求使用许多不同的缓存策略。因此,合理设计缓存使用方式对于提供快速可靠的体验至关重要。例如,如果您的所有应用资源下载速度快、不占用大量空间,并且不需要在每次请求中更新,那么缓存所有资源将是一个有效的策略。另一方面,如果您有需要保持最新版本的资源,则可能需要考虑完全不缓存这些资源。

使用此 API

使用 Cache Storage API 在您的来源中定义一组缓存,每个缓存都通过您可以定义的字符串名称进行标识。通过 caches 对象访问 API,open 方法可用于创建或打开已创建的缓存。open 方法会返回一个针对缓存对象的 promise。

caches.open("pwa-assets")
.then(cache => {
  // you can download and store, delete or update resources with cache arguments
});

下载和存储资源

如需让浏览器下载并存储资源,请使用 addaddAll 方法。add 方法会根据请求或网址数组发出请求并存储一个 HTTP 响应,而 addAll 方法会存储一组 HTTP 响应作为事务。

caches.open("pwa-assets")
.then(cache => {
  cache.add("styles.css"); // it stores only one resource
  cache.addAll(["styles.css", "app.js"]); // it stores two resources
});

缓存存储接口会存储整个响应,包括所有标头和正文。因此,您稍后可以使用 HTTP 请求或网址作为键来检索它。您可以在“提供服务”一章中了解具体操作方法。

何时缓存

在 PWA 中,您可以自行决定何时缓存文件。虽然一种方法是在安装 service worker 时尽可能多地存储资源,但这通常不是最好的方法。缓存不必要的资源会浪费带宽和存储空间,并可能导致应用提供意外的过时资源。

您无需一次性缓存所有资源,可以在 PWA 的生命周期内多次缓存资源,例如:

  • 在安装 Service Worker 时。
  • 首次加载网页后。
  • 当用户导航到某个版块或路线时。
  • 当网络处于空闲状态时。

您可以在主线程中或在 service worker 上下文中请求缓存新文件。

在 Service Worker 中缓存资源

最常见的场景之一是在安装 service worker 时缓存最少的必需资源。为此,您可以使用服务工作线程中 install 事件内的缓存存储接口。

由于服务工作线程可以随时停止,因此您可以请求浏览器等待 addAll promise 完成,以增加存储所有资源并保持应用一致性的机会。以下示例演示了如何使用在服务工作线程事件监听器中收到的事件实参的 waitUntil 方法来执行此操作。

const urlsToCache = ["/", "app.js", "styles.css", "logo.svg"];
self.addEventListener("install", event => {
   event.waitUntil(
      caches.open("pwa-assets")
      .then(cache => {
         return cache.addAll(urlsToCache);
      });
   );
});

waitUntil() 方法会接收一个 promise,并要求浏览器等待 promise 中的任务完成(成功或失败),然后再终止 service worker 进程。您可能需要链接 promise 并返回 add()addAll() 调用,以便将单个结果传递给 waitUntil() 方法。

您还可以使用 async/await 语法处理 promise。在这种情况下,您需要创建一个可以调用 await 的异步函数,并在调用后返回一个对 waitUntil() 的 promise,如以下示例所示:

const urlsToCache = ["/", "app.js", "styles.css", "logo.svg"];
self.addEventListener("install", (event) => {
   let cacheUrls = async () => {
      const cache = await caches.open("pwa-assets");
      return cache.addAll(urlsToCache);
   };
   event.waitUntil(cacheUrls());
});

跨网域请求和不透明响应

您的 PWA 可以从来源和跨网域(例如第三方 CDN 中的内容)下载和缓存资源。对于跨网域应用,缓存互动与同源请求非常相似。系统会执行请求,并将响应的副本存储在缓存中。与其他缓存的资源一样,它只能在应用的来源中使用。

该资源将存储为不透明的响应,这意味着您的代码将无法查看或修改该响应的内容或标头。此外,不透明响应不会在存储 API 中公开其实际大小,从而影响配额。某些浏览器会显示较大的大小,例如 7 MB,无论文件是否只有 1 KB。

更新和删除资产

您可以使用 cache.put(request, response) 更新素材资源,并使用 delete(request) 删除素材资源。

如需了解详情,请参阅 Cache 对象文档

调试缓存空间

许多浏览器都提供了一种方法,可在其开发者工具的“应用”标签页中调试缓存存储的内容。您可以在其中查看当前来源中每个缓存的内容。我们将在工具和调试一章中详细介绍这些工具。

Chrome 开发者工具调试缓存存储区内容。

资源