在某些情况下,Service Worker 可能需要主动与 来通知某个事件。例如:
- 在安装了新版本的 Service Worker 时通知页面,以便页面 可以向用户显示“更新以刷新”按钮,以使用新功能 。
- 通过以下方法让用户了解 Service Worker 端缓存数据发生的变化: 显示指示,如:“此应用现在可以离线使用”或“新版 “可用内容”。
![显示 Service Worker 与页面通信以发送更新的示意图。](https://web.developers.google.cn/static/articles/broadcast-updates-guide/image/diagram-showing-service-25fb181d9fd41.png?authuser=0000&hl=sr)
我们将这些类型的用例称为 Service Worker 不需要接收来自 页面以开始通信“广播更新”。在本指南中,我们将介绍 如何使用标准 API 在页面和 Service Worker 之间实现这种通信, 和 Workbox 库。
生产案例
Tinder
Tinder PWA 使用 workbox-window
监听
页面中的重要 Service Worker 生命周期时刻(“已安装”“受控制”和
“已启用”)。这样,当新的 Service Worker 启动时,它会显示 Update Available
横幅,以便他们可以刷新 PWA 并使用最新功能:
![Tinder 的 Web 应用“有可用更新”的屏幕截图功能](https://web.developers.google.cn/static/articles/broadcast-updates-guide/image/a-screenshot-tinders-we-7235223308143.png?authuser=0000&hl=sr)
松鼠
在 Squoosh PWA 中,当 Service Worker 缓存完所有必需的 素材资源使其可离线使用,系统便会向网页发送消息,显示“可离线使用” 消息框,让用户了解该功能:
<ph type="x-smartling-placeholder">![Squoosh 网络应用“准备离线工作”的屏幕截图功能](https://web.developers.google.cn/static/articles/broadcast-updates-guide/image/a-screenshot-squoosh-web-f8bcc1343566b.png?authuser=0000&hl=sr)
使用 Workbox
监听 Service Worker 生命周期事件
workbox-window
提供一个简单的接口来监听重要的 Service Worker 生命周期
事件。
从本质上讲,该库使用客户端 API,例如
updatefound
和 statechange
并在 workbox-window
对象中提供更高级别的事件监听器,使得
用户使用这些事件
通过以下页面代码,您可以在每次安装新版本的 Service Worker 时进行检测, 以便您将其传达给用户:
const wb = new Workbox('/sw.js');
wb.addEventListener('installed', (event) => {
if (event.isUpdate) {
// Show "Update App" banner
}
});
wb.register();
在缓存数据发生变化时通知页面
Workbox 软件包
workbox-broadcast-update
提供了一种标准方法,可通知窗口客户端缓存的响应已更新。这是
StalewhileRevalidate 的
策略。
要广播最新动态,请在broadcastUpdate.BroadcastUpdatePlugin
Service Worker 端:
import {registerRoute} from 'workbox-routing';
import {StaleWhileRevalidate} from 'workbox-strategies';
import {BroadcastUpdatePlugin} from 'workbox-broadcast-update';
registerRoute(
({url}) => url.pathname.startsWith('/api/'),
new StaleWhileRevalidate({
plugins: [
new BroadcastUpdatePlugin(),
],
})
);
在 Web 应用中,您可以监听这些事件,如下所示:
navigator.serviceWorker.addEventListener('message', async (event) => {
// Optional: ensure the message came from workbox-broadcast-update
if (event.data.meta === 'workbox-broadcast-update') {
const {cacheName, updatedUrl} = event.data.payload;
// Do something with cacheName and updatedUrl.
// For example, get the cached content and update
// the content on the page.
const cache = await caches.open(cacheName);
const updatedResponse = await cache.match(updatedUrl);
const updatedText = await updatedResponse.text();
}
});
使用浏览器 API
如果 Workbox 提供的功能无法满足您的需求,请使用以下浏览器 用于实现“广播更新”的 API:
广播频道 API
Service Worker 创建一个 BroadcastChannel
对象并开始发送
消息。任何有兴趣接收这些消息的上下文(例如,网页)都可以实例化
BroadcastChannel
对象,并实现消息处理程序以接收消息。
要在安装新的 Service Worker 时通知页面,请使用以下代码:
// Create Broadcast Channel to send messages to the page
const broadcast = new BroadcastChannel('sw-update-channel');
self.addEventListener('install', function (event) {
// Inform the page every time a new service worker is installed
broadcast.postMessage({type: 'CRITICAL_SW_UPDATE'});
});
该页面通过订阅 sw-update-channel
来监听这些事件:
// Create Broadcast Channel and listen to messages sent to it
const broadcast = new BroadcastChannel('sw-update-channel');
broadcast.onmessage = (event) => {
if (event.data && event.data.type === 'CRITICAL_SW_UPDATE') {
// Show "update to refresh" banner to the user.
}
};
这是一种简单的技术,但它的局限性在于浏览器支持:在撰写本文时, Safari 不支持此 API。
Client API
客户端 API 提供了一种简单直接的
从 Service Worker 与多个客户端进行通信的方法,方法是迭代
Client
对象。
使用以下 Service Worker 代码将消息发送到上次聚焦的标签页:
// 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'});
}
});
该页面会实现消息处理程序以拦截这些消息:
// Listen to messages
navigator.serviceWorker.onmessage = (event) => {
if (event.data && event.data.type === 'MSG_ID') {
// Process response
}
};
对于将信息广播到多个活动标签页等情况,客户端 API 是一个很好的选择。通过 所有主流浏览器都支持 API,但并非所有主流浏览器都支持。请提前检查浏览器支持情况 使用它。
消息渠道
消息频道要求
初始配置步骤,也就是将端口从页面传递到 Service Worker,以建立
它们之间的通信渠道该页面会实例化 MessageChannel
对象,并将
通过 postMessage()
接口连接到 Service Worker:
const messageChannel = new MessageChannel();
// Init port
navigator.serviceWorker.controller.postMessage({type: 'PORT_INITIALIZATION'}, [
messageChannel.port2,
]);
网页通过实现“onmessage”来监听消息处理程序:
// Listen to messages
messageChannel.port1.onmessage = (event) => {
// Process message
};
Service Worker 接收该端口并保存对它的引用:
// Initialize
let communicationPort;
self.addEventListener('message', (event) => {
if (event.data && event.data.type === 'PORT_INITIALIZATION') {
communicationPort = event.ports[0];
}
});
从此时起,它可以向页面发送消息,方法是在对postMessage()
端口:
// Communicate
communicationPort.postMessage({type: 'MSG_ID' });
由于需要初始化端口,因此 MessageChannel
实现起来可能更为复杂,但
所有主流浏览器均支持这一功能。
后续步骤
在本指南中,我们探讨了 Window 到 Service Worker 通信的一个特定情况: "broadcast updates"。探讨的示例包括监听重要的 Service Worker 生命周期事件,以及向页面传达内容或缓存数据更改方面的信息。您可以这样想 Service Worker 主动与页面通信的更有趣的用例, 之前不会收到任何消息。
如需了解 Window 和 Service Worker 通信的更多模式,请查看:
其他资源
- workbox-window
- Workbox-broadcast-update
- 工作框 4:使用工作框窗口实现“刷新更新版本”流程 模块