您的 Web 应用中的某些资源可能不常用、非常大,或者根据用户的设备(例如自适应图片)或语言变化。在这类情况下,预缓存可能属于反模式,您应改用运行时缓存。
在 Workbox 中,您可以使用 workbox-routing
模块匹配路由,从而处理资源的运行时缓存,并使用 workbox-strategies
模块处理资源的缓存策略。
缓存策略
您可以使用一种内置缓存策略来处理资产的大多数路由。本文档前面的部分对这些内容进行了详细介绍,但下面几部分值得回顾:
- 在重新验证时过时会为请求使用缓存的响应(如果可用),并使用网络的响应在后台更新缓存。因此,如果资源未缓存,则会等待网络响应并使用网络响应。这是一种相当安全的策略,因为它会定期更新依赖于它的缓存条目。其缺点是,它始终会在后台向网络请求资源。
- 网络优先会尝试首先从网络获取响应。如果收到响应,它会将该响应传递给浏览器,并将其保存到缓存。如果网络请求失败,系统将使用上次缓存的响应,从而启用对该资产的离线访问。
- 缓存优先会先检查缓存中是否有响应,然后使用该响应(如果有)。如果请求不在缓存中,则会使用网络,并将任何有效响应添加到缓存中,然后再传递到浏览器。
- 仅限广告网络会强制响应来自网络。
- 仅缓存会强制响应来自缓存。
您可以使用 workbox-routing
提供的方法,将这些策略应用于选择请求。
通过路由匹配应用缓存策略
workbox-routing
公开了 registerRoute
方法以匹配路由,并使用缓存策略对其进行处理。registerRoute
接受 Route
对象,而该对象又接受以下两个参数:
匹配回调首选用于匹配路线,因为它们提供的上下文对象包含 Request
对象、请求网址字符串、提取事件,以及表明相应请求是否为同源请求的布尔值。
然后,处理程序会处理匹配的路由。在以下示例中,系统会创建一个与传入的同源图片请求匹配的新路由,并应用缓存优先,回退到网络策略。
// sw.js
import { registerRoute, Route } from 'workbox-routing';
import { CacheFirst } from 'workbox-strategies';
// A new route that matches same-origin image requests and handles
// them with the cache-first, falling back to network strategy:
const imageRoute = new Route(({ request, sameOrigin }) => {
return sameOrigin && request.destination === 'image'
}, new CacheFirst());
// Register the new route
registerRoute(imageRoute);
使用多个缓存
借助 Workbox,您可以使用捆绑策略中提供的 cacheName
选项,将缓存的响应分桶到单独的 Cache
实例中。
在以下示例中,图片使用 stale-while-revalidate 策略,而 CSS 和 JavaScript 资源则使用缓存优先回退到网络策略。通过添加 cacheName
属性,每个资源的路径会将响应放入单独的缓存中。
// sw.js
import { registerRoute, Route } from 'workbox-routing';
import { CacheFirst, StaleWhileRevalidate } from 'workbox-strategies';
// Handle images:
const imageRoute = new Route(({ request }) => {
return request.destination === 'image'
}, new StaleWhileRevalidate({
cacheName: 'images'
}));
// Handle scripts:
const scriptsRoute = new Route(({ request }) => {
return request.destination === 'script';
}, new CacheFirst({
cacheName: 'scripts'
}));
// Handle styles:
const stylesRoute = new Route(({ request }) => {
return request.destination === 'style';
}, new CacheFirst({
cacheName: 'styles'
}));
// Register routes
registerRoute(imageRoute);
registerRoute(scriptsRoute);
registerRoute(stylesRoute);
![Chrome 开发者工具的应用标签页中缓存实例列表的屏幕截图。显示了三个不同的缓存:一个名为“scripts”,另一个名为“styles”,最后一个名为“images”。](https://developer.chrome.google.cn/static/docs/workbox/caching-resources-during-runtime/image/a-screenshot-a-list-cac-bdd2858b485be.png?authuser=8&hl=hu)
为缓存条目设置过期时间
管理 Service Worker 缓存时,请注意存储空间配额。ExpirationPlugin
可简化缓存维护,并由 workbox-expiration
公开。如需使用该库,请在缓存策略的配置中指定该库:
// sw.js
import { registerRoute, Route } from 'workbox-routing';
import { CacheFirst } from 'workbox-strategies';
import { ExpirationPlugin } from 'workbox-expiration';
// Evict image cache entries older thirty days:
const imageRoute = new Route(({ request }) => {
return request.destination === 'image';
}, new CacheFirst({
cacheName: 'images',
plugins: [
new ExpirationPlugin({
maxAgeSeconds: 60 * 60 * 24 * 30,
})
]
}));
// Evict the least-used script cache entries when
// the cache has more than 50 entries:
const scriptsRoute = new Route(({ request }) => {
return request.destination === 'script';
}, new CacheFirst({
cacheName: 'scripts',
plugins: [
new ExpirationPlugin({
maxEntries: 50,
})
]
}));
// Register routes
registerRoute(imageRoute);
registerRoute(scriptsRoute);
遵守存储空间配额可能会非常复杂。因此,您最好考虑一下可能遇到了存储空间压力的用户,或希望高效利用存储空间的用户。Workbox 的 ExpirationPlugin
对可帮助实现该目标。
跨源注意事项
Service Worker 和跨源资源之间的交互与同源资源之间的交互截然不同。跨域资源共享 (CORS) 很复杂,处理 Service Worker 中的跨域资源也更为复杂。
不透明响应
在 no-cors
模式下发出跨源请求时,响应可存储在 Service Worker 缓存中,甚至可供浏览器直接使用。不过,响应正文本身无法通过 JavaScript 读取。这称为不透明响应。
不透明响应是一种旨在防止检查跨源资源的一项安全措施。您仍然可以针对跨源资源发出请求,甚至可以缓存它们,只是无法读取响应正文,甚至无法读取其状态代码!
请务必选择启用 CORS 模式
即使您加载的跨源资源确实设置了允许读取响应的宽松 CORS 标头,跨域响应的正文可能仍然是不透明的。例如,以下 HTML 会触发 no-cors
请求,而不管设置了什么 CORS 标头,都会导致不透明响应:
<link rel="stylesheet" href="https://example.com/path/to/style.css">
<img src="https://example.com/path/to/image.png">
如需明确触发会生成非不透明响应的 cors
请求,您需要在 HTML 中添加 crossorigin
属性来明确选择启用 CORS 模式:
<link crossorigin="anonymous" rel="stylesheet" href="https://example.com/path/to/style.css">
<img crossorigin="anonymous" src="https://example.com/path/to/image.png">
当 Service Worker 中的路由缓存运行时加载的子资源时,请务必谨记这一点。
Workbox 可能无法缓存不透明响应
默认情况下,Workbox 会谨慎地缓存不透明响应。由于无法检查不透明响应的响应代码,因此如果使用缓存优先或仅缓存策略,则缓存错误响应可能会导致体验持续中断。
如果您需要在 Workbox 中缓存不透明响应,则应使用网络优先或过时时验证策略来处理该响应。是的,这意味着您每次仍会从网络请求资源,但这样可确保失败的响应不会持续存在,并最终会被可用的响应所取代。
如果您使用其他缓存策略并返回不透明响应,则 Workbox 将警告您在开发模式下未缓存响应。
强制缓存不透明响应
如果您绝对确定要使用缓存优先或仅缓存策略来缓存不透明响应,可以通过 workbox-cacheable-response
模块强制 Workbox 执行此操作:
import {Route, registerRoute} from 'workbox-routing';
import {NetworkFirst, StaleWhileRevalidate} from 'workbox-strategies';
import {CacheableResponsePlugin} from 'workbox-cacheable-response';
const cdnRoute = new Route(({url}) => {
return url === 'https://cdn.google.com/example-script.min.js';
}, new CacheFirst({
plugins: [
new CacheableResponsePlugin({
statuses: [0, 200]
})
]
}))
registerRoute(cdnRoute);
不透明响应和 navigator.storage
API
为避免跨网域信息泄露,计算存储配额限制时,系统需为不透明响应的大小增加一个数值。这会影响 navigator.storage
API 报告存储空间配额的方式。
此内边距因浏览器而异,但对于 Chrome 来说,任何一个缓存的不透明响应对总存储空间占用的最小大小为大约 7 兆字节。在确定要缓存多少不透明响应时,您应牢记这一点,因为您可能会比预期要快得多,超出存储空间配额。