缓存存储是一款功能强大的工具。这有助于降低应用对网络状况的依赖性。通过充分利用缓存,您可以使 Web 应用可供离线使用,并在任何网络条件下尽快提供资源。如资产和数据中所述,您可以决定用于缓存必要资产的最佳策略。为了管理缓存,您的 Service Worker 应与 Cache Storage API 交互。
Cache Storage API 可在不同的情境中使用:
- 窗口上下文(PWA 的主线程)。
- Service Worker。
- 您使用的任何其他 worker。
使用 Service Worker 管理缓存的一个好处是,其生命周期与窗口无关,这意味着您不会阻塞主线程。请注意,如需使用 Cache Storage API,大部分环境必须在 TLS 连接下进行。
要缓存的内容
关于缓存,您可能遇到的第一个问题就是要缓存什么。虽然这个问题没有统一的答案,但您可以从渲染界面所需的所有资源着手。
这些资源应包括:
- 主页面 HTML(应用的 start_url)。
- 主界面所需的 CSS 样式表。
- 界面中使用的图片。
- 呈现界面所需的 JavaScript 文件。
- 呈现基本体验所需的数据(例如 JSON 文件)。
- 网络字体。
- 在多页应用中,要快速提供或要在离线状态下提供的其他 HTML 文档。
可离线使用
虽然支持离线访问是渐进式 Web 应用的要求之一,但您必须明白,并非每个 PWA 都需要完整的离线体验,例如云游戏解决方案或加密资产应用。因此,可以提供一个基本的界面来指导用户应对这些情况。
您的 PWA 不应呈现浏览器的错误消息,提示网页呈现引擎无法加载相应页面。而应使用 Service Worker 显示您自己的消息传递,从而避免出现一般性的令人困惑的浏览器错误。
您可以根据 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
});
下载和存储资源
如需要求浏览器下载和存储资源,请使用 add
或 addAll
方法。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
});
Cache Storage 接口存储整个响应,包括所有标头和正文。因此,您稍后可以使用 HTTP 请求或网址作为键进行检索。如需了解具体操作方法,请参阅“投放”章节。
何时缓存
在 PWA 中,您需要决定何时缓存文件。虽然一种方法是在安装 Service Worker 时存储尽可能多的资源,但这通常不是最佳做法。缓存不必要的资源会浪费带宽和存储空间,并且可能会导致您的应用意外传送过时的资源。
您无需一次性缓存所有资源,可以在 PWA 的生命周期内多次缓存资源,例如:
- 安装 Service Worker 时。
- 首次加载网页之后。
- 当用户导航到某个路段或路线时。
- 当网络空闲时。
您可以在主线程或 Service Worker 上下文中请求缓存新文件。
在 Service Worker 中缓存资源
最常见的场景之一是在安装 Service Worker 时缓存最小的资源集。为此,您可以在 Service Worker 的 install
事件中使用 Cache Storage 接口。
由于 Service Worker 线程可能随时停止,因此您可以请求浏览器等待 addAll
promise 执行完毕,以增加存储所有资源并保持应用一致性的机会。以下示例演示了如何使用在 Service Worker 事件监听器中收到的事件参数的 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 的内容。对于跨域应用,缓存交互与同源请求非常相似。系统执行了请求,并将响应的副本存储到您的缓存中。与其他缓存资源一样,它只能在应用的来源中使用。
该资源将作为不透明响应存储,这意味着您的代码将无法查看或修改该响应的内容或标头。此外,不透明响应不会在 Storage API 中公开其实际大小,因而会影响配额。有些浏览器会显示较大的文件(例如 7Mb),无论文件是否只有 1Kb 都是如此。
更新和删除资产
您可以使用 cache.put(request, response)
更新资产,使用 delete(request)
删除资产。
如需了解详情,请参阅缓存对象文档。
调试 Cache Storage
许多浏览器都提供了在开发者工具应用标签页中调试缓存存储空间内容的方法。在这里,您可以看到当前源中每个缓存的内容。我们将在工具和调试一章中详细介绍这些工具。