跳转到内容

Service Worker · API

Cache API

发布于

Cache API 是一个按源隔离、持久保存 Request/Response 对的存储,在 service worker 与页面中都通过 caches 全局对象(一个 CacheStorage)访问。除了你的代码写入或删除、以及存储压力下整源被驱逐之外,里面的内容不会变化。它自 Chrome 43、Edge 16、Firefox 41、Safari 11.1 起可用,且仅限安全上下文(BCD api.CacheStorage)。

// caches 全局对象
const cache = await caches.open(cacheName);
const hit = await caches.match(request, options);
const exists = await caches.has(cacheName);
const names = await caches.keys();
const removed = await caches.delete(cacheName);
// Cache
await cache.add(request);
await cache.addAll([request1, request2]);
await cache.put(request, response);
const response = await cache.match(request, options);
const responses = await cache.matchAll(request, options);
const requests = await cache.keys(request, options);
const deleted = await cache.delete(request, options);

所有方法都返回 Promise。request 参数可以是 Request,也可以是 URL 字符串,后者会相对于 worker 或页面的地址解析。caches.open() 在缓存不存在时会创建它。

CacheStorage 管理带名字的缓存;Cache 保存其中一个缓存的条目。

成员 返回值 行为
caches.open(name) Promise<Cache> 打开或创建指定名字的缓存。名字是任意字符串,这正是版本化命名(shell-v3)得以成立的原因。
caches.match(request, options) Promise<Response | undefined> 按创建顺序搜索该源的所有缓存,以第一个命中兑现。options.cacheName 可把搜索限定在一个缓存内。
caches.has(name)、caches.keys()、caches.delete(name) Promise<boolean>、Promise<string[]>、Promise<boolean> 对整个缓存做盘点与删除。名字不存在时 delete() 兑现为 false。
cache.add(request) Promise<void> 发起请求并存储响应,相当于 fetch() 加 put()。
cache.addAll(requests) Promise<void> 发起全部请求并原子性地存储:任一失败则一条都不存。
cache.put(request, response) Promise<void> 存储你手头已有的响应,不发请求。body 流会被消耗,若还要把响应返回出去,先 clone()。
cache.match(request, options) Promise<Response | undefined> 在本缓存中查找一个条目。
cache.matchAll(request, options) Promise<Response[]> 匹配该请求的全部条目;省略 request 时返回所有条目。
cache.keys(request, options) Promise<Request[]> 已存储的 Request 对象,便于按 URL 清理。
cache.delete(request, options) Promise<boolean> 删除匹配的条目;至少删掉一条时兑现为 true。

options 是 CacheQueryOptions 字典:ignoreSearch 比较时忽略查询串,ignoreMethod 允许非 GET 请求匹配已存的 GET 条目,ignoreVary 跳过 Vary 头的匹配。条目以 URL 与方法为键,并且默认遵守已存响应的 Vary 头,所以 Accept-Language 不同的请求可能查不到一条在 DevTools 里明明存在的条目。

拒绝原因是 DOMException 或 TypeError。

  • TypeError:请求方法不是 GET(add、addAll、put),请求 scheme 不是 http: 或 https:,响应状态为 206,或其 Vary 头含 *。Chromium 的报错包括 Partial response (status code 206) is unsupported 与 Vary header contains *。
  • add() 与 addAll() 的 TypeError:某个响应不是 ok(状态不在 200 到 299 之间)或请求失败。Chromium 报 Request failed,因此预缓存列表里一个 404 就会让整个 addAll() 中止;若它运行在 install 的 waitUntil() 里,安装也随之失败。
  • QuotaExceededError:put()、add()、addAll() 写入时该源的存储配额已耗尽。Chromium 在配额核算中把每条 opaque(no-cors、状态 0)响应按约 7 MB 计入,所以在接近满盘的设备上,几条第三方资源就可能触发它。
  • SecurityError:在非安全上下文访问 caches。在 localhost 以外的 http: 源上,window.caches 为 undefined,worker 中的 self.caches 也不可用。

下面两个示例分别是版本化缓存模式的 install 与 activate 两半,之后是一个针对 caches 缺失环境的检测示例。

在 install 中原子性地预缓存应用壳

Section titled “在 install 中原子性地预缓存应用壳”

在 event.waitUntil() 内使用 addAll(),这样任何一个资源失败都会把有问题的 worker 挡在 active 槽位之外。

const SHELL = 'shell-v3';
const ASSETS = ['/', '/app.js', '/app.css', '/offline.html'];
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(SHELL).then((cache) => cache.addAll(ASSETS))
);
});

由于 addAll() 是原子的,shell-v3 要么完整要么不存在;/app.css 返回 404 会让 Promise 被拒绝、安装失败,而原来处于 active 状态的 worker 继续服务。

旧缓存不会自动清理。新 worker 激活后,删除所有不等于当前名字的缓存。

self.addEventListener('activate', (event) => {
event.waitUntil(
caches.keys().then((names) =>
Promise.all(names.filter((n) => n !== SHELL).map((n) => caches.delete(n)))
)
);
});

这段代码要放在 activate 而不是 install 里:install 期间旧 worker 可能仍在用你要删除的那个缓存为页面提供服务。

检测支持,并在不支持时只在线运行

Section titled “检测支持,并在不支持时只在线运行”

非安全源与没有该 API 的浏览器中没有 caches。下面的回退跳过 service worker 注册,并告知应用不要宣称支持离线。

export function initOffline() {
if ('caches' in window && 'serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js');
return { offline: true };
}
// 没有 Cache Storage:只在线运行,并隐藏"可离线使用"标记。
return { offline: false };
}

直接调用 caches.open() 的页面也应同样防护;在 http: 源上直接调用会在任何 Promise 产生之前就抛出。

规范

规范状态
无。