默认情况下,较新的 Service Worker

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() 时,或者通过 pushsync 事件“唤醒”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”。