Cache API:异步请求的缓存控制

作者 | Brilliant Open Web团队

编辑 |  Brilliant Open Web团队

本文将介绍 C ache API 的功能和用法,并结合代码进行演示,主要探究  C ache API 打开缓存对象、添加缓存、查找缓存和删除缓存的操作,指出代码实践中需要注意的问题 。关于   C ache API 的更多新特性,还需要在具体项目中进行实践和探索。

Cache API 是什么

提到异步请求,开发者可能对 Fetch API 应该已经比较了解了。Fetch API 根据 Request 对象描述的请求信息发起网络请求并返回 Promise 对象,当请求得到响应时 Promise 执行 resolve 并传回 Response 对象,这使得资源的请求与响应变得更加简洁方便。

而 Cache API 则是提供了一系列方法来缓存并管理这些 Request/Response 对象 — 可以把 Cache API 理解为资源请求/响应的缓存仓库。尽管它被定义在 Service Worker 的标准中,但它仍可以独立使用,不必一定要配合 Service Worker 使用。

需要注意的是,在新版本的浏览器中,Cache API 只保存 HTTPS 服务的请求,不支持 HTTP 缓存头。对于缓存数据,除非开发者明确删除,否则缓存数据永远不会过期。也正因为如此,开发者需要定期清理缓存,防止缓存量超过浏览器限定的大小而出现不必要的数据丢失。

兼容性检测

开发者可以在浏览器主线程或者 Service Worker 线程(独立于浏览器主线程的工作线程,有独立的执行上下文)中,通过判断全局变量上是否存在 caches 对象来检测:


if (‘caches’ in window) {
// Service Worker 中使用 ‘caches’ in self 判断
console.log(‘当前环境支持 Cache API’)
}

打开 Cache 对象

通过 caches.open() 方法可以打开一个 Cache 对象,接收的参数为 cacheName ,表示要打开的 Cache 对象的名称,以 v1 为例:

caches.open('v1').then(cache => {/* 获得 Cache 对象 */})

在 Chrome 中打开开发者工具,切换到 Application – Cache Storage 选项卡,可以看到,执行 caches.open() 方法时,会在 Cache Storage 下边建立同名仓库,每个仓库里面的内容就是对应的 Cache 对象管理的缓存资源,现在由于没有添加缓存所以内容为空:

添加缓存

Cache 对象提供了 put() add() addAll() 三个方法来添加或者覆盖原有缓存。需要注意的是,这些添加缓存的方法只会对 GET 请求起作用。

Cache.put( )

Cache API 会以请求的 Request 对象作为键,响应的 Response 对象作为值进行存储:


// 假设打开 ‘v1’ 仓库
caches.open(‘v1’).then(cache => {
cache.put(
new Request(‘/data.json’),
new Response(JSON.stringify({name: ‘lilei’}))
)
})

如上就给 v1 仓库写入了 ‘/data.json’ 请求和响应的缓存。通过开发者工具可以看到仓库中新增的缓存条目信息:

由于 Request 和 Response 是基于数据流实现的,在进行缓存的时候开发者需要格外留意 Response 对象的响应体 数据是否已经被读取。

Cache.add( ) 和 Cache.addAll( )

add() 和   addAll() 方法的功能等同于先调用 fetch() , 然后使用 cache.put() 将 Response 添加到 cache。 add() 只能请求和缓存一个资源,而 addAll() 能够抓取并缓存多个资源。这两个方法使得缓存服务端资源变得更为简单:


cache.add(‘/data.json’).then(() => {/* 缓存成功 */})
cache.addAll([
‘/data.json’,
‘/info.txt’
]).then(() => {/* 缓存成功 */})

add() addAll() 方法会缓存 Response.ok 为 true 的响应。对于跨域请求返回的不透明的 Response 对象,同样也会缓存下来。

查找缓存

cache.match() cache.matchAll() 可以实现缓存的查找。 match() 会返回第一个匹配条件的缓存结果,而 matchAll() 则会返回所有满足匹配条件的缓存结果。下面举例说明如何查找“/data.json”的缓存资源,代码如下:


// 使用 match() 进行查找,假设 cache 由 caches.open(‘v1’) 打开
cache.match(‘/data.json’).then(response => {
// 使用 matchAll() 查找则可以通过 if (!responses.length) 判断资源匹配成功与否
if (response == null) {
// 没有匹配到任何资源
} else {
// 成功匹配资源
}
})

上述查找方法可以传入第二参数来控制匹配过程,比如设置 ignoreSearch 参数,会在匹配过程中忽略 URL 中的 Search 部分:


// 假设缓存的请求 URL 为 /data.json?v=1
cache.match(‘/data.json?v=2’, {ignoreSearch: true}).then(response => {
// /data.json?v=1 匹配成功
})

在上面的例子当中,缓存的 URL 和用于匹配的 URL 都带有 Search 参数,但由于配置了 ignoreSearch 值为 true,因此最终仍然匹配成功。

删除缓存

通过 cache.delete() 方法清理缓存。比如要删除前面添加成功的“/data.json”请求,代码如下所示:


cache.delete(‘/data.json’).then(success => {
// 将打印 true,代表删除成功,false 则代表失败
console.log(success)
})

如果删除一个未被缓存的请求,则执行删除后返回的 ‘success’ 为 false。

在调用 cache.delete() 时可以传入第二参数去控制删除操作中如何匹配缓存,其格式与 match() matchAll() 等方法的第二参数完全一致。

查找匹配的请求

前面介绍的 match() matchAll() 方法会返回匹配的响应,如果要获取匹配的请求,可以通过 cache.keys() 方法实现:


cache.keys(‘/data.json’, {ignoreSearch: true}).then(requests => {
// requests 可能包含 /data.json、/data.json?v=1、/data.json?v=2 等请求对象
// 如果匹配不到任何请求,则返回空数组

})

如果没有传入任何第二参数, cache.keys() 会默认返回当前 Cache 对象中缓存的全部请求。

总结

本篇文章介绍了 Cache API 的基本概念和使用方法,也对常用的增删改查操作进行了示例。随着更多浏览器对 Cache API 的支持,相信 Cache API 在未来的 Web 缓存中会发挥更大的优势。

本文内容主要来自开源书籍 《PWA 应用实战》 。该书由百度 Web 生态团队撰写与分享,记录了团队过去两年积累的 PWA 方面的经验,欢迎对 Web 和 PWA 有浓厚兴趣的读者加入我们,一起来维护这本书。

Brilliant Open Web 

BOW(Brilliant Open Web)团队,是一个专门的Web技术建设小组,致力于推动 Open Web 技术的发展,让Web重新成为开发者的首选。

BOW 关注前端,关注Web;剖析技术、分享实践;谈谈学习,也聊聊管理。

关注 OpenWeb开发者,让我们一起推动 OpenWeb技术的发展!

OpenWeb开发者

ID:BrilliantOpenWeb

技术连接世界,开放赢得未来