跳转到内容

Service Worker 中的 API

Service Worker 静态路由 API

发布于 更新于

Service Worker 静态路由 API 让 service worker 在安装时声明哪些请求应由浏览器直接从网络或缓存获取,而不必为取回它们而运行 worker。声明的形式是在 install 处理器中传给 event.addRoutes() 的一组规则,每条规则把一个针对请求的条件与响应应当来自的来源配成一对。

addRoutes(routerRules)

routerRules 是单个规则对象或由它们组成的数组,方法返回一个以 undefined 兑现的 promise。该方法位于 InstallEvent 上,因此可从 install 处理器访问,一组规则也就属于安装它的那个 worker 版本。规范指出 event.addRoutes(rules) 默认延长事件的生命周期,就像调用了 event.waitUntil(promise) 一样。

BCD api.InstallEvent.addRoutes 记录了从 Chrome 123 和 Safari 27 起的支持,并镜像到它列出的基于 Chromium 的浏览器与 Safari 衍生版本;Firefox 没有支持,BCD 指向 Mozilla bug 1855580 说明实现状态。Chrome 的发布文章把该 API 描述为从 Chrome 123 起可用,之前自 Chrome 116 起以 registerRouter() 之名进行过源试用,该方法被这篇文章标记为已弃用,计划在 Chrome 125 中移除。

一条规则是 { condition, source }。下面这些条件键来自 MDN 的 addRoutes() 参考页;当一个条件中同时出现多个键时,资源必须满足全部条件才匹配该规则。

条件键 取值 匹配什么
urlPattern 一个 URLPattern 实例,或可传入其构造函数的输入模式 匹配规则的那些 URL。不允许正则捕获组,因此 URLPattern.hasRegExpGroups 必须为 false。
requestMethod 字符串,如 "get"、"put"、"head" 发送请求所用的 HTTP 方法。
requestMode 字符串,如 "same-origin"、"no-cors"、"cors" 请求具有的模式。
requestDestination 字符串,如 "audio"、"document"、"script"、"worker" 请求的目标,即请求的内容类型。
runningStatus "running" 或 "not-running" service worker 所需的运行状态。
or 条件对象数组 其中一组嵌套条件。or 内的条件与其他条件互斥。
not 一个条件对象 必须明确不被满足的条件。not 内的条件与其他条件互斥。

source 是一个枚举值或一个对象。MDN 列出的枚举值为 "cache"、"network"、"fetch-event" 与 "race-network-and-fetch-handler",最后一个会同时尝试网络与 fetch 事件处理器,并采用先完成的那一个。MDN 把 "fetch-event" 与 runningStatus 条件搭配使用:worker 运行时经由它加载资源,不运行时回退到网络上的静态路由。对象形式携带单个 cacheName 属性来指名一个缓存;MDN 指出如果该缓存不存在,浏览器会改用网络,这样在网络可用时仍能取到所请求的资源。

MDN 列出的 TypeError 成因有:routerRules 内有无效的规则对象、worker 没有 fetch 事件处理器时 source 为 "fetch-event",以及 or 与另一类条件组合使用。

  • 无效规则会让携带它的那次调用被拒绝,所以在上线前先校验生成出来的规则列表。
  • "fetch-event" 与 "race-network-and-fetch-handler" 这两个来源依赖已注册的 fetch 处理器。addRoutes() 算法 规定:当 source 为 "fetch-event" 或 "race-network-and-fetch-handler",而 service worker 要处理的事件类型集合不包含 fetch 时,返回一个以 TypeError 拒绝的 promise。source 为 "network"、"cache" 或 cacheName 对象的规则不在该步骤的指名范围内。
  • 条件中 or 与同级键并列时必须改写为两条规则,每条对应一个分支并重复该同级键。MDN 自己给出的改写示例就把 urlPattern 与 requestMethod 配对了两次。
  • 当 Check Router Registration Limit 算法(在 worker 现有路由规则列表的副本追加新规则后运行)返回 false 时,规范同样以 TypeError 拒绝该 promise。

空的条件对象会被拒绝,而不是成为匹配一切的规则,这正是上面的第一条字符串。

下面这些示例是多数应用需要的形态:在某个路径上绕过 worker、在方法存在时才声明路由,以及用一个命名缓存服务图片扩展名。

在 worker 处于冷状态时把路径交给网络

Section titled “在 worker 处于冷状态时把路径交给网络”

条件把 URL 模式与 runningStatus 组合起来,因此该规则在 service worker 不运行时生效,而这正是会付出启动开销的情形。

addEventListener('install', (event) => {
event.addRoutes({
condition: {
urlPattern: '/articles/*',
runningStatus: 'not-running',
},
source: 'network',
});
});

worker 正在运行时这条规则不匹配,请求会到达 fetch 处理器,因此同一路径上的缓存优先策略在热导航中仍然适用。Chrome 的发布文章展示了镜像写法:runningStatus: "running" 配 source: "fetch-event"。

在方法存在时才声明路由,并带回退

Section titled “在方法存在时才声明路由,并带回退”

addRoutes() 是事件对象上的方法,所以使用前可以检测。缺失分支不是降级模式:它就是静态路由出现之前 service worker 的行为,一切由 fetch 处理器决定。

addEventListener('install', (event) => {
if (!('addRoutes' in event)) {
// 该事件上没有 addRoutes():把路由交给 fetch 处理器。
return;
}
event
.addRoutes([
{ condition: { urlPattern: '/form/*', requestMethod: 'post' }, source: 'network' },
{ condition: { requestDestination: 'font' }, source: { cacheName: 'fonts' } },
])
.catch((error) => {
console.error('static routes rejected, using the fetch handler instead', error);
});
});

保留 catch。该方法以拒绝而不是同步抛出的方式报告失败,因此没有处理器的拒绝会让应用仅靠 fetch 处理器运行,并表现为一个未处理的 promise 拒绝。

用一个命名缓存服务两种图片扩展名

Section titled “用一个命名缓存服务两种图片扩展名”

or 接受一组条件且与其他条件互斥,所以扩展名放进数组里,共享的来源留在数组外面。

addEventListener('install', (event) => {
event.addRoutes({
condition: {
or: [{ urlPattern: '*.png' }, { urlPattern: '*.jpg' }],
},
source: { cacheName: 'pictures' },
});
});

若要把同样这两个模式收窄到 GET 请求,就写两条规则,各自带自己的 urlPattern 与 requestMethod,这也是 MDN 针对这种情况给出的改写方式。

Chrome DevTools 会显示浏览器实际注册了什么:Chrome 的发布文章指出,已注册的路由规则显示在 Application 面板的 Service Worker 标签中;在 Network 面板里,匹配到已注册规则的请求会在 size 列中标出,指针悬停时显示已注册的路由 ID。

规范

规范状态
Service Workers: the addRoutes() methodW3C 草案