渐进式web应用开发—Service Worker 与页面通信(七)

我们前面的demo使用了 WindowClient 或 service worker 对象发送消息,并且只看到了 postMessage()只接收了第一个参数。

但是我们的postMessage方法可以接收第二个参数,我们可以使用该参数来保持双方之间的通信渠道打开。可以来回发送消息。

那么这种通信 是通过 MessageChannel 对象处理的。我们可以通过构造函数 MessageChannel() 可以创建一个消息通道,该实列会有2个属性,分别为 port1 和 port2; 如下代码所示:

var msg = new MessageChannel();
console.log(msg);

打印信息如下所示:

如上图我们可以看到,该对象有 onmessage 和 onmessageerror 两个属性是两个回调方法。我们可以使用 MessagePort.postMessage 方法发送消息的时候,我们就可以通过另一个端口的 onmessage 来监听该消息。

也就是说消息通道是有两个口子,那么这两个口子分别是 port1 和 port2。这两个口子可以相互发送消息,port1口子发送的消息,我们可以在port2口子中接收到消息。

比如如下代码:

var msg = new MessageChannel();
var p1 = msg.port1;
var p2 = msg.port2;

// 使用p1口子监听消息
p1.onmessage = function(msg) {
  console.log('接收到的消息:' + msg.data);
}

// 使用p2口子发送消息
p2.postMessage("hello world");

打印信息如下所示:

如上我们可以看到,MessageChannel对象有两个口子,分别为 port1 和 port2; 我们在port2上使用 postMessage 发送消息,我们可以在 port1上监听到该消息。

现在我们把该 MessageChannel 消息通道使用到我们的 service worker 当中来,当我们从窗口向service worker 通信时(或者反正都可以),我们可以在窗口中创建一个新的 MessageChannel 对象,并且通过 postMessage 将其中一个口子传递给 serviceworker, 当消息到达后,就可以在service worker 中访问端口了。如下:

首先我们在我们的 main.js(入口文件)添加如下代码:

var msgChan = new MessageChannel();
var p1 = msgChan.port1;

// 使用p1口子监听消息
p1.onmessage = function(msg) {
  console.log('接收到的消息:' + msg.data);
}

var msg = {
  name: 'kongzhi',
  age: 31,
  value: 2
};

if ("serviceWorker" in navigator && navigator.serviceWorker) {
  navigator.serviceWorker.controller.postMessage(msg, [msgChan.port2]);
}

然后在我们的 service worker.js 中添加如下代码:

// service worker 代码
self.addEventListener("message", function(event) {
  var data = event.data;
  var port = event.ports[0];
  if (data.name === 'kongzhi') {
    port.postMessage(data.value * 2);
  }
});

然后在页面上会打印如下信息:

如上代码我们可以看到,我们在main.js 代码中创建了一个新的 MessageChannel, 并且在port1中的口子上添加了事件监听器。如果收到任何消息就会打印出来,然后我们就会使用 navigator.serviceWorker.controller.postMessage 代码向 service worker发送一条消息。同时将 MessageChannel 第二个口子传递过去,这边使用了一个数组传递过去,以便我们在service worker中通过0或者多个端口进行通信。

在service worker.js 中,我们监听了message事件,当检测到该事件的时候,我们使用 event.data 获取到消息的内容,和页面的端口,并且检测该消息的 name 属性 等于 ‘kongzhi’ 这个字符串的话,那么我们就使用第二个口子 port2发送一个消息过去,那么在main.js 中,我们使用第一个口子 port1 来监听该消息,然后就能接收到消息来了,最后打印信息了,如上所示。

如上demo我们演示了 使用 MessageChannel 来实现两个口子(port1, port2) 之间通信的问题。那么现在我们使用 MessageChannel 如何在页面和service worker 之间保持连续通信通道打开。