在某些情况下,Web 应用可能需要在 页面和 Service Worker。
例如:在播客 PWA 中,用户可以构建一项功能,让用户下载 离线使用,让 Service Worker 定期向网页发送进度信息,以便主 线程可以更新界面。
在本指南中,我们将探索在 Google Cloud 之间实现双向通信的不同方式, Window 和 服务 通过探索工作器 Workbox 库以及 一些高级用例。
使用 Workbox
workbox-window
是一组
Workbox 库中
在窗口上下文中运行Workbox
类提供了一个 messageSW()
方法,用于向实例的已注册 Service Worker 发送消息,以及
等待响应。
以下页面代码将创建一个新的 Workbox
实例并向 Service Worker 发送消息
以获取其版本:
const wb = new Workbox('/sw.js');
wb.register();
const swVersion = await wb.messageSW({type: 'GET_VERSION'});
console.log('Service Worker version:', swVersion);
Service Worker 在另一端实现消息监听器,并响应注册的 Service Worker:
const SW_VERSION = '1.0.0';
self.addEventListener('message', (event) => {
if (event.data.type === 'GET_VERSION') {
event.ports[0].postMessage(SW_VERSION);
}
});
该库在后台使用一个浏览器 API,我们将在下一部分进行介绍:消息 渠道, 更便于使用,同时利用宽浏览器 支持该 API 所具有的各种功能。
使用浏览器 API
如果 Workbox 库无法满足您的需求,有几个较低级别的 API 可供使用 在页面和 Service Worker 之间实现“双向”通信。它们有一些相似之处 差异:
相似之处:
- 在所有情况下,通信都从一端通过
postMessage()
接口开始,然后接收 另一方面,您可以实现message
处理程序。 - 事实上,所有可用的 API 都让我们能够实现相同的用例, 在某些情况下可能会简化开发工作。
差异:
- 他们通过不同的方式来识别沟通的另一方:其中一些人使用 显式引用其他上下文,而其他上下文可通过代理隐式通信 该对象实例化。
- 支持的浏览器因具体情况而异。
广播频道 API
浏览器支持
- <ph type="x-smartling-placeholder">
- <ph type="x-smartling-placeholder">
- <ph type="x-smartling-placeholder">
- <ph type="x-smartling-placeholder">
Broadcast Channel API 允许通过 BroadcastChannel 在浏览上下文之间进行基本通信 对象的操作。
如需实现它,首先必须为每个上下文创建一个具有相同 ID 的 BroadcastChannel
对象
并通过它收发消息:
const broadcast = new BroadcastChannel('channel-123');
BroadcastChannel 对象公开 postMessage()
接口,用于向任何监听器发送消息
上下文:
//send message
broadcast.postMessage({ type: 'MSG_ID', });
任何浏览器上下文都可以通过 BroadcastChannel
的 onmessage
方法监听消息
对象:
//listen to messages
broadcast.onmessage = (event) => {
if (event.data && event.data.type === 'MSG_ID') {
//process message...
}
};
如您所见,由于没有对特定上下文的显式引用,因此无需获取 引用 Service Worker 或任何特定的客户端。
其缺点是,在撰写本文时,该 API 支持 Chrome、Firefox 但 Safari 等其他浏览器则不支持 。
Client API
浏览器支持
- <ph type="x-smartling-placeholder">
- <ph type="x-smartling-placeholder">
- <ph type="x-smartling-placeholder">
- <ph type="x-smartling-placeholder">
借助客户端 API,您可以获取
引用表示 Service Worker 控制的活跃标签页的所有 WindowClient
对象。
由于页面由单个 Service Worker 控制,因此它会监听消息并将消息发送到
通过 serviceWorker
接口直接激活 Service Worker:
//send message
navigator.serviceWorker.controller.postMessage({
type: 'MSG_ID',
});
//listen to messages
navigator.serviceWorker.onmessage = (event) => {
if (event.data && event.data.type === 'MSG_ID') {
//process response
}
};
同样,Service Worker 通过实现 onmessage
监听器来监听消息:
//listen to messages
self.addEventListener('message', (event) => {
if (event.data && event.data.type === 'MSG_ID') {
//Process message
}
});
为了与其任何客户端进行反向通信,Service Worker 获取一个数组,
WindowClient
对象(通过执行)
方法,例如
Clients.matchAll()
和
Clients.get()
。然后它可以
postMessage()
:
//Obtain an array of Window client objects
self.clients.matchAll(options).then(function (clients) {
if (clients && clients.length) {
//Respond to last focused tab
clients[0].postMessage({type: 'MSG_ID'});
}
});
Client API
是与 Service Worker 中的所有活动标签页轻松通信的好选项
编写代码。该 API 受到各大主流
浏览器,
但并非所有其方法都可用,因此请务必先检查浏览器支持,然后再
在网站上植入代码
消息渠道
浏览器支持
- <ph type="x-smartling-placeholder">
- <ph type="x-smartling-placeholder">
- <ph type="x-smartling-placeholder">
- <ph type="x-smartling-placeholder">
消息频道要求 定义端口并将其从一个上下文传递到另一个上下文,以建立双向通信 。
为了初始化渠道,页面会实例化 MessageChannel
对象并使用该对象
向已注册的 Service Worker 发送端口。该页面还实现了以下对象上的 onmessage
监听器:
以便从另一个上下文接收消息:
const messageChannel = new MessageChannel();
//Init port
navigator.serviceWorker.controller.postMessage({type: 'PORT_INITIALIZATION'}, [
messageChannel.port2,
]);
//Listen to messages
messageChannel.port1.onmessage = (event) => {
// Process message
};
Service Worker 接收该端口,保存对该端口的引用,并使用它向另一个端口发送消息。 侧面:
let communicationPort;
//Save reference to port
self.addEventListener('message', (event) => {
if (event.data && event.data.type === 'PORT_INITIALIZATION') {
communicationPort = event.ports[0];
}
});
//Send messages
communicationPort.postMessage({type: 'MSG_ID'});
目前,所有主要语言都支持 MessageChannel
浏览器。
高级 API:后台同步和后台提取
在本指南中,我们探索了实现双向通信技术的方式, 简单用例,例如传递描述要执行的操作的字符串消息,或传递网址列表 将一个上下文缓存到另一个上下文。在本节中,我们将探讨两个 API,用于处理特定的 场景:网络连接中断和下载时间长。
后台同步
浏览器支持
- <ph type="x-smartling-placeholder">
- <ph type="x-smartling-placeholder">
- <ph type="x-smartling-placeholder">
- <ph type="x-smartling-placeholder">
聊天应用可能需要确保消息永不因网络连接状况不佳而丢失。通过 Background Sync API 可用来 在用户连接稳定时延迟重试操作。这有助于确保 无论用户想要发送什么内容,它都会实际发送出去。
该页面不会注册 postMessage()
接口,而是注册 sync
:
navigator.serviceWorker.ready.then(function (swRegistration) {
return swRegistration.sync.register('myFirstSync');
});
然后,Service Worker 会监听 sync
事件以处理消息:
self.addEventListener('sync', function (event) {
if (event.tag == 'myFirstSync') {
event.waitUntil(doSomeStuff());
}
});
函数 doSomeStuff()
应返回一个 promise,表明它无论成功还是失败
尝试。如果满足,则同步完成。如果失败,系统会安排另一次同步
重试。重试同步也会等待连接,并采用指数退避算法。
在执行操作之后,Service Worker 便可与页面进行反通信, 更新界面。
Google 搜索会使用后台同步功能来保留由于网络连接不良而失败的查询,然后重试 在用户上线后使用它们。操作完成后,它们会将结果传达给 通过 Web 推送通知向用户发送:
后台提取
浏览器支持
- <ph type="x-smartling-placeholder">
- <ph type="x-smartling-placeholder">
- <ph type="x-smartling-placeholder">
- <ph type="x-smartling-placeholder">
对于相对较短的工作量(如发送消息或要缓存的网址列表),选项 都是不错的选择。如果任务耗时过长,浏览器将终止服务 否则就会给用户隐私和电池带来风险。
后台提取 API 允许您将长任务分流给 Service Worker,例如下载电影、播客或关卡 。
要从页面与 Service Worker 通信,请使用 backgroundFetch.fetch
,而不是
postMessage()
:
navigator.serviceWorker.ready.then(async (swReg) => {
const bgFetch = await swReg.backgroundFetch.fetch(
'my-fetch',
['/ep-5.mp3', 'ep-5-artwork.jpg'],
{
title: 'Episode 5: Interesting things.',
icons: [
{
sizes: '300x300',
src: '/ep-5-icon.png',
type: 'image/png',
},
],
downloadTotal: 60 * 1024 * 1024,
},
);
});
BackgroundFetchRegistration
对象允许网页监听 progress
事件,以便跟踪
下载进度:
bgFetch.addEventListener('progress', () => {
// If we didn't provide a total, we can't provide a %.
if (!bgFetch.downloadTotal) return;
const percent = Math.round(
(bgFetch.downloaded / bgFetch.downloadTotal) * 100,
);
console.log(`Download progress: ${percent}%`);
});
<ph type="x-smartling-placeholder">
后续步骤
在本指南中,我们探讨了页面与 Service Worker 之间的最常见通信情形 (双向通信)。
很多时候,一个用户可能只需要一个上下文来相互通信, 响应。请查看以下指南,了解如何在 来自和传出 Service Worker 的网页,以及用例和生产示例: