【问题标题】:Service Worker - Wait for clients.openWindow to complete before postMessageService Worker - 在 postMessage 之前等待 clients.openWindow 完成
【发布时间】:2018-10-01 22:59:51
【问题描述】:

我正在使用 service worker 来处理后台通知。当我收到一条消息时,我正在使用self.registration.showNotification(title, { icon, body }) 创建一个新的Notification。我正在使用self.addEventListener('notificationclick', ()=>{}) 关注通知上的点击事件。单击时,我正在检查是否有任何 WindowClient 已打开,如果是,我将获取其中一个窗口客户端并在其上调用 postMessage 以将数据从通知发送到应用程序以允许应用程序处理通知。如果没有打开的窗口,我将调用openWindow,一旦完成,我将使用postMessage 将数据发送到该窗口。

event.waitUntil(
    clients.matchAll({ type: 'window' }).then((windows) => {
        if (windows.length > 0) {
            const window = windows[0];
            window.postMessage(_data);
            window.focus();
            return;
        }
        return clients.openWindow(this.origin).then((window) => {
            window.postMessage(_data);
            return;
        });
    })
);

我面临的问题是openWindow 中的postMessage 调用永远不会被传递。我猜这是因为 WindowClient 上的 postMessage 调用发生在页面完成加载之前,所以 eventListener 还没有注册来监听该消息?对吗?

如何从服务人员打开一个新窗口并将消息发送到该新窗口。

【问题讨论】:

  • 嗨,@procedurally-generated 你的问题得到答案了吗?
  • 你解决了吗?
  • @Oliver 不,只是添加了延迟。在允许页面加载 3/4 秒后执行 postMessage。
  • 这是一种解决方法,但不能是唯一的解决方案...

标签: firebase-cloud-messaging service-worker service-worker-events


【解决方案1】:

我遇到了同样的问题。我的错误是我在窗口上注册了事件处理程序。但它应该像这样在 service worker 上注册:

// next line doesn't work
window.addEventListener("message", event => { /* handler */ });

// this one works
navigator.serviceWorker.addEventListener('message', event => { /* handler */ });

请参阅以下页面中的示例:
https://developer.mozilla.org/en-US/docs/Web/API/Clients
https://developer.mozilla.org/en-US/docs/Web/API/Client/postMessage

UPD:澄清一下,这段代码进入了新打开的窗口。已在 Chromium v​​.66 中检查。

【讨论】:

    【解决方案2】:
    clients.openWindow(event.data.url).then(function(windowClient) {
            // do something with the windowClient.
          });
    

    【讨论】:

    • 请补充说明。
    • @Zibri 如果您查看我的问题中的示例代码,您会发现这已经是我正在做的事情。这样做的问题是代码是在窗口打开后执行的,而不是在页面加载后执行的。这意味着代码在页面注册消息侦听器之前执行,因此该帖子消息永远不会被新选项卡捕获。
    【解决方案3】:

    我也偶然发现了这个问题,使用超时是反模式,也可能导致延迟大于 chrome 的 10 秒限制,可能会失败。

    我所做的是检查是否需要打开一个新的客户端窗口。 如果我在 clients 数组中没有找到任何匹配项 - 这是瓶颈,您需要等到页面加载完毕,这可能需要一些时间,并且 postMessage 将无法正常工作。

    对于这种情况,我在服务工作者中创建了一个简单的全局对象,该对象在特定情况下被填充,例如:

    const messages = {};
    ....
    // we need to open new window 
    messages[randomId] = pushData.message; // save the message from the push notification
    await clients.openWindow(urlToOpen + '#push=' + randomId);
    ....
    

    在加载的页面中,在我的 React 应用程序中,我等待我的组件已安装,然后我运行一个函数来检查 URL 是否包含“#push=XXX”哈希,提取随机 ID,然后回消息给 Service Worker 以向我们发送消息。

    ...
    if (self.location.hash.contains('#push=')) {
      if ('serviceWorker' in navigator && 'Notification' in window && Notification.permission === 'granted') {
         const randomId = self.locaiton.hash.split('=')[1];
         const swInstance = await navigator.serviceWorker.ready;
         if (swInstance) {
            swInstance.active.postMessage({type: 'getPushMessage', id: randomId});
         }
         // TODO: change URL to be without the `#push=` hash ..
    }
    

    最后我们在 service worker 中添加一个消息事件监听器:

    self.addEventListener('message', function handler(event) {
        if (event.data.type === 'getPushMessage') {
            if (event.data.id && messages[event.data.id]) {
                // FINALLY post message will work since page is loaded
                event.source.postMessage({
                    type: 'clipboard',
                    msg: messages[event.data.id],
                });
                delete messages[event.data.id];
            }
        }
    });
    

    messages 我们的“全局”不是持久的,这很好,因为当推送通知到达时服务工作者“唤醒”时我们才需要它。

    提供的代码是代码,要指出的是解释这个想法,这对我有用。

    【讨论】:

    • 这是一个很好的解决方法。迄今为止我见过的最好的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-26
    • 2017-02-02
    相关资源
    最近更新 更多