tl;dr
从 Chrome 68 开始,默认情况下,检查 Service Worker 脚本的更新的 HTTP 请求将不再由 HTTP 缓存处理。这种方法解决了常见的开发者痛点,即在 Service Worker 脚本上无意中设置 Cache-Control
标头可能会导致更新延迟。
如果您已通过使用 Cache-Control: max-age=0
传送 /service-worker.js
脚本来为其停用 HTTP 缓存,则应该不会因为新的默认行为而有任何变化。
此外,从 Chrome 78 开始,字节间比较将应用于通过 importScripts()
在 Service Worker 中加载的脚本。对已导入的脚本所做的任何更改都将触发 Service Worker 更新流程,就像对顶级 Service Worker 的更改一样。
背景
每次导航到 Service Worker 范围内的新页面、从 JavaScript 明确调用 registration.update()
时,或者通过 push
或 sync
事件“唤醒”Service Worker 时,浏览器将并行请求最初传递到 navigator.serviceWorker.register()
调用的 JavaScript 资源,以查找对 Service Worker 脚本的更新。
在本文中,我们假设其网址为 /service-worker.js
,并且包含对 importScripts()
的单个调用,该调用会加载在 Service Worker 内运行的其他代码:
// Inside our /service-worker.js file:
importScripts('path/to/import.js');
// Other top-level code goes here.
具体变化
在 Chrome 68 之前,对 /service-worker.js
的更新请求将通过 HTTP 缓存发出(与大多数提取一样。这意味着,如果脚本最初是使用 Cache-Control:
max-age=600
发送的,则接下来 600 秒(10 分钟)内的更新不会发送到网络,因此用户可能无法收到最新版本的 Service Worker。但是,如果 max-age
大于 86400(24 小时),则会被视为 86400,以避免用户永久被特定版本卡住。
从 68 版开始,请求对 Service Worker 脚本的更新时将忽略 HTTP 缓存,因此现有 Web 应用可能会发现其 Service Worker 脚本的请求频率有所增加。对 importScripts
的请求仍将通过 HTTP 缓存进行。但这只是默认设置。您可以使用新的注册选项 updateViaCache
来控制此行为。
updateViaCache
现在,开发者可以在调用 navigator.serviceWorker.register()
时传入一个新选项:updateViaCache
参数。它接受以下三个值之一:'imports'
、'all'
或 'none'
。
这些值决定了在发出 HTTP 请求以检查更新的 Service Worker 资源时,浏览器的标准 HTTP 缓存是否发挥作用以及如何发挥作用。
如果设置为
'imports'
,则在检查/service-worker.js
脚本的更新时绝不会查询 HTTP 缓存,但会在提取任何导入的脚本(在我们的示例中为path/to/import.js
)时查询 HTTP 缓存。这是默认设置,与从 Chrome 68 开始的行为一致。如果设置为
'all'
,则在为顶级/service-worker.js
脚本以及在 Service Worker 内部导入的任何脚本(例如path/to/import.js
)发出请求时,系统会查询 HTTP 缓存。此选项对应于 Chrome 68 之前的 Chrome 中的行为。如果设置为
'none'
,则在针对顶级/service-worker.js
或任何导入的脚本(例如假设的path/to/import.js
)发出请求时,将不会查询 HTTP 缓存。
例如,以下代码将注册一个 Service Worker,并确保在检查 /service-worker.js
脚本的更新或者通过 /service-worker.js
内的 importScripts()
引用的任何脚本的更新时,绝不会查询 HTTP 缓存:
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/service-worker.js', {
updateViaCache: 'none',
// Optionally, set 'scope' here, if needed.
});
}
检查导入的脚本是否有更新
在 Chrome 78 之前,通过 importScripts()
加载的任何 Service Worker 脚本都只能检索一次(首先检查 HTTP 缓存或通过网络检查,具体取决于 updateViaCache
配置)。完成初始检索后,该数据将由浏览器在内部存储,且永远不会被重新提取。
强制已安装的 Service Worker 获取已导入的脚本的更改的唯一方法是更改脚本的网址,通常通过添加 semver 值(例如 importScripts('https://example.com/v1.1.0/index.js')
)或包含内容的哈希值(例如 importScripts('https://example.com/index.abcd1234.js')
)。更改导入网址的附带效应是顶层 Service Worker 脚本的内容更改,进而触发数据流。
从 Chrome 78 开始,每次对顶级 Service Worker 文件执行更新检查时,都会同时进行检查,以确定所有已导入的脚本的内容是否已更改。根据所使用的 Cache-Control
标头,如果 updateViaCache
设置为 'all'
或 'imports'
(默认值),这些导入的脚本检查可能由 HTTP 缓存完成,如果 updateViaCache
设置为 'none'
,这些检查可能会直接针对网络。
如果对已导入的脚本的更新检查导致与 Service Worker 之前存储的数据存在逐字节差异,则会反过来触发完整的 Service Worker 更新流程,即使顶级 Service Worker 文件保持不变也是如此。
Chrome 78 的行为与 Firefox 几年前在 Firefox 56 中实现的行为一致。Safari 也已经实现了此行为。
开发者需要做什么?
如果您已经通过使用 Cache-Control: max-age=0
(或类似值)传送 /service-worker.js
脚本来有效停用该脚本的 HTTP 缓存,那么应该不会因为新的默认行为而有任何变化。
如果您在启用 HTTP 缓存的情况下传送 /service-worker.js
脚本(无论是有意的还是由于它只是托管环境的默认),您可能会看到针对您的服务器发出的 /service-worker.js
的其他 HTTP 请求增加,这些请求过去由 HTTP 缓存执行。如果您想继续允许 Cache-Control
标头值影响 /service-worker.js
的新鲜度,则需要在注册 Service Worker 时开始明确设置 updateViaCache: 'all'
。
考虑到旧版浏览器可能存在长尾用户,我们仍建议您继续在 Service Worker 脚本上设置 Cache-Control: max-age=0
HTTP 标头,即使新版浏览器可能会忽略它们。
开发者可以借此机会决定是否立即为导入的脚本明确停用 HTTP 缓存,并在适当的情况下将 updateViaCache: 'none'
添加到其 Service Worker 注册中。
传送导入的脚本
从 Chrome 78 开始,开发者可能会看到更多针对通过 importScripts()
加载的资源的 HTTP 请求,因为系统现在会检查这些请求是否有更新。
如果您想避免这些额外的 HTTP 流量,请在传送网址中包含 semver 或哈希值的脚本时设置长期有效的 Cache-Control
标头,并依赖于 'imports'
的默认 updateViaCache
行为。
或者,如果您希望检查导入的脚本是否存在频繁更新,请务必使用 Cache-Control: max-age=0
或使用 updateViaCache: 'none'
来提供这些脚本。
深入阅读
建议所有将任何内容部署到 Web 的开发者阅读 Jake Archibald 所著的“Service Worker Lifecycle”和“Caching best practices & max-age intochas”。