跳转到内容

Cache Storage:离线 PWA 背后的请求/响应存储

发布于

一句话: Cache Storage 是面向 Request/Response 对、按源限定的可编程存储——service worker 用它在离线时提供资源与 API 响应。与浏览器自动管理的 HTTP 缓存不同,由你显式决定它 保存什么。

Cache Storage 对比 HTTP 缓存与 IndexedDB

Section titled “Cache Storage 对比 HTTP 缓存与 IndexedDB”
  • HTTP 缓存由浏览器自动管理,且与 Cache Storage 相互独立;Cache API 不遵循 HTTP 缓存 响应头。你无法直接读写 HTTP 缓存。
  • Cache Storage 是一个你直接掌控的存储:你显式地 put、match、delete 整个 Response 对象,以 Request 为键。它让 service worker 的 fetch 处理器能够在无网络时应答请求。
  • IndexedDB 存储结构化记录与 blob,而非 HTTP 响应。应用数据(草稿、排队的变更)用 IndexedDB;应用外壳与已取资源用 Cache Storage。
  • CacheStorage —— 在窗口、Worker 与 service worker 中以 caches 暴露。它是命名 Cache 对象的注册表:caches.open(name)、caches.has(name)、caches.delete(name)、 caches.keys(),以及在所有缓存中跨库查找的便捷方法 caches.match()。
  • Cache —— 单个命名的请求→响应条目存储。关键方法:
    • cache.put(request, response) —— 存入你已有的一对。
    • cache.add(request) / cache.addAll(requests) —— 一步取回并存储。
    • cache.match(request) / cache.matchAll() —— 查找已存储的响应。
    • cache.delete(request) —— 移除一个条目。

Cache 存储 Response 对象。由于响应体只能被读取一次,当你还想把响应返回给页面时,请存储 一个克隆(response.clone())。

caches 在窗口、Web Worker 中可用,并且——关键在于——在 service worker 中可用,这正是让 service worker 的 fetch 事件能在网络不可用时从缓存应答的原因。在 install 期间预存应用 外壳,然后在 fetch 中按你选定的策略从缓存提供,并回退到(或从)网络更新。

const CACHE = 'shell-v3';
const ASSETS = ['/', '/app.css', '/app.js', '/offline.html'];
self.addEventListener('install', (event) => {
event.waitUntil(caches.open(CACHE).then((cache) => cache.addAll(ASSETS)));
});
self.addEventListener('activate', (event) => {
// 删除旧缓存,避免新部署永远提供陈旧文件。
event.waitUntil(
caches.keys().then((names) =>
Promise.all(names.filter((n) => n !== CACHE).map((n) => caches.delete(n)))
)
);
});
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request).then((hit) => hit || fetch(event.request))
);
});

在正常使用下,Cache Storage 不会自行移除你的条目——你放入的内容会一直存在,直到你删除它。 这使得一套刻意的版本管理方案不可或缺:

  • 给缓存名加上版本(shell-v3)。资源变化时提升名称。
  • 在 activate 中删除被取代的缓存(如上),让上一版本的文件被回收,而非不断累积。
  • 谨慎对待不透明响应。 跨源 no-cors 请求会产生状态为 0 的不透明响应,它只能用 put 存储;仅在必须时才缓存它们。

Cache Storage 的数据计入由 Storage API 管理的源存储用量,与 IndexedDB 等其他 web 存储一起 计算。当达到配额上限时,浏览器可能逐出某个源的存储,因此把缓存的资源当作可丢弃的,并 随时准备重新获取。在数据必须经受逐出之处,用 navigator.storage.persist() 申请持久存储。 (完整的配额与逐出模型见存储持久化参考。)

Cache Storage API(CacheStorage 与 Cache)在当前主流浏览器上获得支持,并且可从 service worker 访问,使其成为 PWA 离线资源的基线存储。

  • 请求/响应类资源用 Cache Storage;结构化应用数据用 IndexedDB。
  • 若还要把响应返回给页面,缓存前先 clone()。
  • 给缓存名加版本,资源变化时提升名称。
  • 在 service worker 的 activate 事件中删除旧缓存。
  • 把缓存视为可丢弃;为重新获取做好设计,数据必须经受逐出时调用 persist()。
  • 谨慎缓存不透明的跨源响应——你无法检查它们。