如何与 Service Worker 通信

更新日期: 2020-03-04阅读: 2.5k标签: 通信

Service Worker 很棒。它们使 Web 开发人员可以实现以前原生应用专有的类似功能。这类功能是例如推送通知后台同步的离线功能。

它们是渐进式 Web 应用的核心。但是在设置它们之后,似乎很难完成涉及与 Web 应用交互的更复杂的事情。

在本文中,我将展示可用的选择并最后进行比较。


Service Worker 与 Web Worker

如果你查看 Service Workers 的 api将会看到 Web Worker 和 Service Worker 有非常相似的接口。尽管有相似之处,但它们的意图和功能却大不相同:


  • Service Worker 可以拦截请求并将其替换为自己缓存中的项目,因此它们的行为就像是代理服务器。他们为 Web 应用提供了“离线功能”。

它们可以在多个标签中使用,甚至在所有标签关闭后仍然可以使用。

  • 另一方面,Web worker 有不同的用途。它们为单线程 JavaScript 语言提供了多线程功能,并用于执行计算繁重的任务,这些任务不应干扰 UI 的响应能力。

它们仅限于一个标签 

两者的共同点是它们无权访问 dom,无法使用 postMessage API 进行通信。你可以将它们看作是具有扩展功能的 Web Worker。

如果你想了解有关它们更多信息,请查看这个对话,尽管有些陈旧,但可以个很好的概述这个话题。到 2020 年,Service Workers 的浏览器支持有了很大的改进。


如何与 Service Worker 通信

选择要向其发送消息的 Service Worker

对于任何来源,都可以有多个 Service Worker。以下内容返回当前控制页面的活动 Service Worker:

navigator.serviceWorker.controller

如果要访问其他 Service Worker,则可以通过 registration 接口访问,该借口使你可以访问以下位置的 Service Worker 状态:

  • ServiceWorkerRegistration.installing
  • ServiceWorkerRegistration.waiting - 已安装此 Service Worker,但尚未激活
  • ServiceWorkerRegistration.active -此Service Worker正在控制当前页面

你可以通过几种不同的方式访问 registration 接口。其中有一个 navigator.serviceWorker.ready。它将返回一个可以通过注册解决的 promise:

navigator.serviceWorker.ready.then((registration) => {
  // At this point, a Service Worker is controlling the current page
});

如果你想了解有关 Service Worker 生命周期的更多信息,请查看这篇文章:(https://bitsofco.de/the-servi...)。

发送信息

正如我已经提到的,Service Worker 通过 postMessage API 进行通信。这不仅允许他们与JavaScript主线程交换数据,而且还可以将消息从一个Service Worker发送到另一个Service Worker。

// app.js - Somewhere in your web app
navigator.serviceWorker.controller.postMessage({
  type: 'MESSAGE_IDENTIFIER',
});
// service-worker.js
// On the Service Worker side we have to listen to the message event
self.addEventListener('message', (event) => {
  if (event.data && event.data.type === 'MESSAGE_IDENTIFIER') {
    // do something
  }
});

这种单向通信的用例是在等待服务的 Service Worker 中调用 skipWaiting,然后将其传递为活动状态并控制页面。这已在 Create-react-App 附带的 Service Worker 中实现。我用此技术在渐进式 Web 应用中显示更新通知,我在这篇文章(https://felixgerschau.com/cre...)中进行了解释。

但是如果你想将消息发送回 Window上下文甚至其他 Service Worker,该怎么办?


Service Worker - Client 通信

有好几种方法可以将消息发送到 Service Worker 的客户端:

  • Broadcast Channel API 允许浏览上下文之间进行通信。此 API 允许上下文之间进行通信,而无需引用。Chrome、Firefox 和 Opera 目前支持该功能。能够建立多对多广播通信。
  • MessageChannel API 它可用于在 Window 和 Service Worker 上下文之间建立一对一通信。
  • Service Worker 的 Clients 接口。它可用于向 Service Worker 的一个或多个客户端进行广播。

我将为你提供每个方法的简短示例,然后将它们进行比较,以查看哪种方法最适合你的用例。

我没有包含 FetchEvent.respondWith(),因为这仅适用于获取事件,而且目前不受 Safari 浏览器支持。

使用 MessageChannel API

顾名思义,MessageChannel API 设置了一个可以发送消息的通道。

该实现可以归结为3个步骤。

  1. 在两侧设置事件侦听器以接收 'message' 事件
  2. 通过发送 port 并将其存储在 Service Worker 中,建立与 Service Worker 的连接。
  3. 使用存储的 port 回复客户端

也可以添加第四步,如果你想通过在 Service Worker 中调用 port.close() 来关闭连接的话。

在实践中看起来像这样:

// app.js - somewhere in our main app
const messageChannel = new MessageChannel();

// First we initialize the channel by sending
// the port to the Service Worker (this also
// transfers the ownership of the port)
navigator.serviceWorker.controller.postMessage({
  type: 'INIT_PORT',
}, [messageChannel.port2]);

// Listen to the response
messageChannel.port1.onmessage = (event) => {
  // Print the result
  console.log(event.data.payload);
};

// Then we send our first message
navigator.serviceWorker.controller.postMessage({
  type: 'INCREASE_COUNT',
});
// service-worker.js
let getVersionPort;
let count = 0;
self.addEventListener("message", event => {
  if (event.data && event.data.type === 'INIT_PORT') {
    getVersionPort = event.ports[0];
  }

  if (event.data && event.data.type === 'INCREASE_COUNT') {
    getVersionPort.postMessage({ payload: ++count });
  }
}

使用 Broadcast API

Broadcast API 与 MessageChannel 非常相似,但是它消除了将端口传递给 Service Worker 的需求。

在这个例子中,我们看到只需要在两侧建立一个有相同名称 count-channel 的通道。

我们可以将相同的代码添加到其他 WebWorker 或 Service Worker,后者也将接收所有这些消息。

在这里,我们从上方看到了相同的例子,但用了 Broadcast API:

// app.js
// Set up channel
const broadcast = new BroadcastChannel('count-channel');

// Listen to the response
broadcast.onmessage = (event) => {
  console.log(event.data.payload);
};

// Send first request
broadcast.postMessage({
  type: 'INCREASE_COUNT',
});
// service-worker.js
// Set up channel with same name as in app.js
const broadcast = new BroadcastChannel('count-channel');
broadcast.onmessage = (event) => {
  if (event.data && event.data.type === 'INCREASE_COUNT') {
    broadcast.postMessage({ payload: ++count });
  }
};

使用 Client API

Client API 也不需要传递对通道的引用。

在客户端,我们侦听 Service Worker 的响应,在 Service Worker 中,用 self.clients.matchAll 函数提供给我们的过滤器选项,选择要发送响应的客户端。

// app.js
// Listen to the response
navigator.serviceWorker.onmessage = (event) => {
  if (event.data && event.data.type === 'REPLY_COUNT_CLIENTS') {
    setCount(event.data.count);
  }
};

// Send first request
navigator.serviceWorker.controller.postMessage({
  type: 'INCREASE_COUNT_CLIENTS',
});
// service-worker.js
// Listen to the request
self.addEventListener('message', (event) => {
  if (event.data && event.data.type === 'INCREASE_COUNT') {
    // Select who we want to respond to
    self.clients.matchAll({
      includeUncontrolled: true,
      type: 'window',
    }).then((clients) => {
      if (clients && clients.length) {
        // Send a response - the clients
        // array is ordered by last focused
        clients[0].postMessage({
          type: 'REPLY_COUNT',
          count: ++count,
        });
      }
    });
  }
});


总结

postMessage API提供了一个简单灵活的接口,使我们可以将消息发送给 Service Worker。

Broadcast Channel API 是最容易使用的选项,但不幸的是,它的浏览器支持并不是很好。

在剩下的两个中,我更喜欢 Client API,因为这不需要将引用传递给 Service Worker。

作者:Felix Gerschau
翻译:疯狂的技术宅
原文:https://felixgerschau.com/


链接: https://fly63.com/article/detial/8495

vue.js $emit/$on的用法和理解_vue组件之间数据传输通信

每个 Vue 实例都实现了事件接口vm.$emit( event, arg ) 触发当前实例上的事件;vm.$on( event, fn )监听event事件后运行。实例说明:Vuejs 用$emit与$on来进行兄弟组件之间的数据传输通信,Vuejs 用$emit与$on来进行跨页面之间的数据传输通信

两个浏览器窗口间通信总结

两个浏览器窗口间通信:一个窗口更新localStorage,另一个窗口监听window对象的storage事件来实现通信;所有的WebSocket都监听同一个服务器地址,利用send发送消息,利用onmessage获取消息的变化;借助iframe 或 window.open;HTML5 中的 Web Worker 可以分为两种不同线程类型

前端跨页面通信,你知道哪些方法?

在浏览器中,我们可以同时打开多个Tab页,每个Tab页可以粗略理解为一个“独立”的运行环境,即使是全局对象也不会在多个Tab间共享。然而有些时候,我们希望能在这些“独立”的Tab页面之间同步页面的数据、信息或状态。

Vue组件之间通信的七种方式

使用Vue也有很长一段时间,但是一直以来都没对其组件之间的通信做一个总结,这次就借此总结一下。父子组件之间的通信props和$emit 父组件通过props将数据下发给props

浏览器标签页之间通信的实现

前端开发过程中,总是避免不了要进行前端标签页之间的通信,最经典的例子莫过于音乐播放网站中,当第一次点击播放列表中的歌曲时,它会打开一个新的标签页进行播放,而当在列表中再次点击歌曲播放时

基于 ThinkJS 的 WebSocket 通信详解

我们的项目是基于 ThinkJS + Vue 开发的,最近实现了一个多端实时同步数据的功能,所以想写一篇文章来介绍下如何在 ThinkJS 的项目中利用 WebSocket 实现多端的实时通信。ThinkJS 是基于 Koa 2 开发的企业级 Node.js 服务端框架

Socket是如何通信的?

其实服务器的处理和客户端大同小异,分三个逻辑分支:检索成功,用检索到的Socket来处理接收报文;检索失败,服务器侦听(listen)目的端口,创建全新的Socket服务客户;检索失败,服务器没有侦听目的端口,丢弃处理

vue中使用v-model完成组件间的通信

如何实现两个组件之间的双向传递呢?即,在父组件中修改了值,子组件会立即更新。在子组件中修改了值,父组件中立即更新。vue中有一个很神奇的东西叫v-model,它可以完成我们的需求。

vue父子组件通信高级用法

vue项目的一大亮点就是组件化。使用组件可以极大地提高项目中代码的复用率,减少代码量。但是使用组件最大的难点就是父子组件之间的通信。父组件通过$refs调用子组件的方法。 以上就是父子组件通信的方式

微服务的三种通信方法

在微服务架构的世界中,我们通过一系列服务构建应用。集合中的每项服务都符合以下标准:松散耦合、可维护和可测试、可以独立部署,微服务架构中的每个服务都解决了应用中的业务问题

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!