Chrome ServiceWorker postMessage

use*_*502 7 message google-chrome postmessage service-worker


我尝试在WebApp和相应的ServiceWorker之间postMessage.serviceWoker已成功注册并正常工作.
不幸的是我发现了一些奇怪的行为:
1.navigator.serviceWorker.controller始终为null.
2.在serviceWorker端,我以这种方式实现了postMessage:

self.addEventListener('message', function (evt) {
console.log('postMessage received', evt);
});
Run Code Online (Sandbox Code Playgroud)

不幸的是,发回原始evt.origin =""和evt.source = null的重要字段不包含所需的值.但我总是收到发送的evt.data.

你知道怎么回帖吗?

非常感谢你!
和我

Man*_*anu 16

演示者的响应发送回受控应用程序的一种方法在此演示中显示,并按以下方式完成(我还没有对此进行过测试,但演示效果很好,代码似乎与规范一致) .

在主页面中:

function sendMessage(message) {
  // This wraps the message posting/response in a promise, which will
  // resolve if the response doesn't contain an error, and reject with
  // the error if it does. If you'd prefer, it's possible to call
  // controller.postMessage() and set up the onmessage handler
  // independently of a promise, but this is a convenient wrapper.
  return new Promise(function(resolve, reject) {
    var messageChannel = new MessageChannel();
    messageChannel.port1.onmessage = function(event) {
      if (event.data.error) {
        reject(event.data.error);
      } else {
        resolve(event.data);
      }
    };

    // This sends the message data as well as transferring
    // messageChannel.port2 to the service worker.
    // The service worker can then use the transferred port to reply
    // via postMessage(), which will in turn trigger the onmessage
    // handler on messageChannel.port1.
    // See
    // https://html.spec.whatwg.org/multipage/workers.html#dom-worker-postmessage
    navigator.serviceWorker.controller.postMessage(message, [messageChannel.port2]);
  });
}
Run Code Online (Sandbox Code Playgroud)

并在服务工作者代码中:

self.addEventListener('message', function(event) {
  event.ports[0].postMessage({'test': 'This is my response.'});
});
Run Code Online (Sandbox Code Playgroud)

然后,您应该能够使用sendMessage函数返回的promise来使用来自服务工作者的响应执行您想要的操作.

  • 有没有办法在不发送第一条消息的情况下进行单向通信 - 我想在每个'install'事件上从服务工作者向客户端发送消息(客户端不需要发送第一条消息) (3认同)

Ser*_*ndt 8

广播频道 API 更简单

与其他方法相比,使用Broadcast Channel API在 Service Worker 与其客户端之间来回发送信息要容易得多。

这是广播的工作原理

在 Service Worker 和客户端中定义一个新的广播通道。

const channel4Broadcast = new BroadcastChannel('channel4');
Run Code Online (Sandbox Code Playgroud)

要在工作程序或客户端中发送广播消息:

channel4Broadcast.postMessage({key: value});
Run Code Online (Sandbox Code Playgroud)

要在工作程序或客户端中接收广播消息:

channel4Broadcast.onmessage = (event) => {
    value = event.data.key;
}
Run Code Online (Sandbox Code Playgroud)

广播还允许在客户端的Web Worker、客户端和 Service Worker之间发送消息。

警告

但这种简单性是有代价的。按照目前的情况,Service Worker 不会醒来接收广播消息。因此,它仅在 Service Worker 处于运行(活动或等待)状态时起作用;即当客户端可见时。尽管如此,这可能仍然有用,例如广播版本号或网络推送等。


All*_*uis 5

@GalBracha问你是否可以在没有先发送消息的情况下与客户沟通 - 是的!这是一个快速示例,说明当我想在收到推送通知时向客户发送消息时(不是当用户点击通知,而是当服务工作人员收到事件时)我是如何做到的:

在您的客户端js(服务人员注册后等):

// navigator.serviceWorker.addEventListener('message', ... ) should work too
navigator.serviceWorker.onmessage = function (e) {
    // messages from service worker.
    console.log('e.data', e.data);
};
Run Code Online (Sandbox Code Playgroud)

在您的服务工作者中,响应某些事件(可能是安装事件):

// find the client(s) you want to send messages to:
self.clients.matchAll(/* search options */).then( (clients) => {
    if (clients && clients.length) {
        // you need to decide which clients you want to send the message to..
        const client = clients[0];
        client.postMessage("your message");
    }
Run Code Online (Sandbox Code Playgroud)

诀窍(显然是?)将消息事件的监听器附加到serviceWorker对象,而不是窗口对象.

  • @GalBracha:另请参阅/sf/answers/2457619111/(如果发回到(非受控)注册页面,则“ includeUn受控”选项会有所帮助。) (2认同)