上周我写了一篇关于渐进式网络的缓存策略的文章,该策略首先使用缓存,然后进入网络,在网页之间共享消息并ServiceWorker协调对缓存内容的更新。swivel今天我将描述用于简化 ServiceWorkers 消息传递的库的内部工作原理。

事实证明,您可以通过多种不同的方式在 aServiceWorker及其控制的网页之间共享消息。

  • 客户端可能想要向a 发送消息ServiceWorker、询问某事或通知某事– 这是“单播”(1 对 1)
  • A可能想要向客户端ServiceWorker发送回复– 单播
  • AServiceWorker可能想要向其控制下的每个客户端发送更新– 一条“广播”(一对多)消息
  • AServiceWorker可能想要向发起请求的客户端发送更新– 单播fetch

向 ServiceWorker 发送消息

为了向 a 发送消息ServiceWorker,您可以使用类似于以下代码片段的代码。

worker.postMessage(data);

默认情况下,您可能希望使用navigator.serviceWorker.controller当前页面的活动 ServiceWorker 实例来控制其请求,如worker我个人还没有发现需要与控制器以外的工作人员交谈,这就是为什么这是worker您在swivel. 话虽这么说,它们有用例,这就是为什么会有swivel.at(worker),但让我们回到我们的重点领域。

工作人员可以设置一个事件处理程序来处理data我们从网页发布的有效负载。

self.addEventListener('message', function handler (event) {
  console.log(event.data);
});

一旦您想要向员工发送不同类型的消息,这就会成为一个问题。您必须求助于消息路由约定。一个常见的约定是为您的消息定义一个信封,其中包含一个command属性,因此现在您将发送如下所示的消息。

worker.postMessage({ command: 'deleteCache', key: key });

然后,工作人员需要使用命令处理路由器进行更新,在更简单的情况下,一堆就if可以了。您可以看到代码如何开始被实现细节稀释。

self.addEventListener('message', function handler (event) {
  if (event.data.command === 'deleteCache') {
    caches.delete(event.data.key);
  }
});

得到回复ServiceWorker

如果你希望工作人员能够回复消息,那么事情就会变得非常难看、非常快,主要是因为浏览器还没有实现最终的 API。
目前,在客户端,您需要设置一个MessageChannel监听器,绑定一个侦听器,并在发布消息时port1传递该监听器。port2

var messageChannel = new MessageChannel();
messageChannel.port1.addEventListener('message', replyHandler);
worker.postMessage(data, [messageChannel.port2]);
function replyHandler (event) {
  console.log(event.data); // this comes from the ServiceWorker
}

ServiceWorker侧面来看,这也不是那么有趣。您必须引用port2using ,因为它是与消息一起传递的位置零messageChannelevent.ports[0]端口。ports

self.addEventListener('message', function handler (event) {
  event.ports[0].postMessage(data); // handle this using the replyHandler shown earlier
});

浏览器最终将有一个替代event.source方案,不需要我们在页面上做任何事情。不幸的是,这还没有实现,所以我们现在只能求助于它。event.ports[0]ServiceWorkerMessageChannelMessageChannel

从 ServiceWorker 向每个客户端广播

这种情况很简单,但与我们刚才谈到的两种情况也有很大不同。而且,老实说,非常冗长。当然,ServiceWorker这一切都与表达能力和能够满足多种不同用例的能力有关,我们都在看似简单但巧妙复杂的界面(如 AppCache 清单)中看到了隐藏的邪恶。

话虽这么说,必须输入这些内容很糟糕。图书馆肯定会帮助消除痛苦,同时ServiceWorker可以继续成为现代网络所提供的最强大的功能。

self.clients.matchAll().then(all => all.map(client => client.postMessage(data)));

要监听来自 a 的这些消息ServiceWorker,您可以在网页中注册一个事件监听器,如下所示。请注意如何将侦听器添加到navigator.serviceWorker特定的worker (例如)上,而不是添加到特定的(例如navigator.serviceWorker.controller上。

navigator.serviceWorker.addEventListener('message', function handler (event) {
  console.log(event.data);
});

您可能希望保留对您感兴趣的引用worker,然后按 过滤消息侦听器event.source。这样,您就可以避免从您期望收到消息的工作人员之外的工作人员广播消息。

navigator.serviceWorker.addEventListener('message', function handler (event) {
  if (event.source !== worker) {
    return;
  }
  console.log(event.data);
});

双通道fetch请求

向请求源发送更新fetch可能是ServiceWorker网页之间通信最有趣的用例。向请求源发送更新fetch使得立即使用缓存然后尽快发送更新变得如此有效。

self.on('fetch', function handler (event) {
  // reply to client here
});

最终,event将有一个clientId属性来标识请求来自的客户端。client然后我们可以使用如下代码向using发送消息client.postMessage。目前还没有浏览器实现event.clientId

self.on('fetch', function handler (event) {
  event.respondWith(caches.match(event.request));
  fetch(event.request).then(response => response.json()).then(function (data) {
    self.clients.match(event.clientId).then(client => client.postMessage(data));
  });
});

您仍然可以使用前面讨论的广播机制,但它会发送到每个客户端,而不仅仅是事件的起源fetch。目前,更好的选择可能是在加载后从页面发出另一个请求,也许添加一个自定义标头,指示我们应该fetchServiceWorker侧面强制使用 a。

旋转让您的生活更轻松

如果您想避免所有这些琐碎的知识,您可能会喜欢swivel罗恩·斯旺森。

罗恩·斯旺森旋转以避免人体接触
罗恩·斯旺森旋转以避免人体接触

旋转有很多好处。API 统一为事件发射器风格。在客户端上,您可以向ServiceWorker以下地址发送消息。

swivel.emit('remove-cache', 'v1');

然后在工作线程上,您只需使用匹配的 API 来监听即可。

swivel.on('remove-cache', (context, key) => caches.delete(key));

如果worker需要回复,可以使用context.reply.

swivel.on('remove-cache', function handler (context, key) {
  caches.delete(key).then(function () {
      context.reply('removed-cache', 'ok', 'whatever');
  });
});

然后,发送消息的客户端只要监听该removed-cache事件就可以处理回复。请注意,此处的 API 与用于侦听 ServiceWorker 上的事件的 API 相同。

swivel.on('removed-cache', function handler (context, success, metadata) {
  // do something else
});

ServiceWorker有重要公告时,可以向每个客户广播。

swivel.broadcast('announcement', { super: '!important' });

swivel.on可以在客户端使用完全相同的 API 来监听广播消息。除了 之外swivel.on,还有swivel.once绑定一次性事件处理程序和swivel.off删除事件处理程序。

最后,正如我们之前提到的,您可以与不同的 ServiceWorker 实例进行交互。swivel.*您可以不直接使用API,swivel.at(worker).*而是使用。从客户端使用发送的消息swivel.at(worker).emit只会发送到使用worker注册的侦听器,并且广播的消息worker将仅可供使用 注册的侦听器使用swivel.at(worker).on。这种范围界定有助于防止在淘汰旧工人和安装新工人时发生事故。

您可以在 GitHub 上查看完整文档。

ServiceWorkerMessageChannel, & postMessage. 天啊!

via https://ponyfoo.com/articles/serviceworker-messagechannel-postmessage

ServiceWorker、MessageChannel 和 postMessage
标签: