跳转到内容

Clients API:service worker 对客户端对象的访问能力

一句话: Clients 接口在 service worker 内部通过 self.clients 访问,提供对符合查询选项的 Client 对象(窗口、worker)的访问——通过 matchAll({ includeUncontrolled: true }),还可以包含与该 service worker 共享同一 storage key/同源、但未被其控制、也未与其注册关联的客户端。

该特性仅在 service worker 中可用。

// 在 service worker 内部
self.clients;
方法 返回值 作用
Clients.get(id) Promise<Client | undefined> 返回与给定 id 匹配的 Client;若没有匹配的客户端则解析为 undefined
Clients.matchAll(options) Promise<Array<Client>> 返回一个 Client 对象数组;默认只返回受控客户端,options 参数可控制返回哪些类型的客户端,包括尚未受控的客户端。
Clients.openWindow(url) Promise<WindowClient | null> 为给定的 URL 打开一个新的浏览器窗口;若平台无法为其返回同源的 WindowClient,则解析为 null
Clients.claim() 一个解析为 undefinedPromise 让处于激活状态的 service worker 将自己设置为其作用域内所有客户端的控制者。

查找已打开的聊天窗口并聚焦,或在未找到时打开一个新窗口,然后向其发送消息:

addEventListener("notificationclick", (event) => {
event.waitUntil(
(async () => {
const allClients = await clients.matchAll({
includeUncontrolled: true,
});
let chatClient;
// 看看是否已经有一个聊天窗口打开:
for (const client of allClients) {
const url = new URL(client.url);
if (url.pathname === "/chat/") {
// 太好了,用它!
client.focus();
chatClient = client;
break;
}
}
// 如果没有找到已有的聊天窗口,
// 打开一个新的:
chatClient ??= await clients.openWindow("/chat/");
// 向该客户端发送消息:
chatClient.postMessage("New chat messages!");
})(),
);
});

Clients 接口属于 Baseline 广泛可用:它已经在众多设备和浏览器版本中得到充分确立并可正常使用,自 2018 年 4 月起已在各浏览器中可用。

  • 当 service worker 需要查找它尚未控制的窗口时,使用带 includeUncontrolled 选项的 clients.matchAll()——默认情况下 matchAll() 只返回受控客户端。
  • 处理 clients.get(id) 解析为 undefinedclients.openWindow(url) 解析为 null 的情况(即找不到匹配客户端时)。
  • 如果 service worker 需要立即接管现有客户端,而不是等到下一次导航,在 activate 事件处理函数中调用 clients.claim()
  • 仅在用户手势(例如点击通知)的响应中使用 clients.openWindow(),遵循上面示例展示的模式。
  • Navigation preload — 另一个通过类似作用域 API 暴露的 service worker 能力