6

我正在使用服务人员来处理后台通知。当我收到一条消息时,我正在Notification使用self.registration.showNotification(title, { icon, body }). 我正在使用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;
        });
    })
);

我面临的问题是,postMessage里面的电话openWindow永远不会送达。我猜这是因为在页面完成加载之前发生的postMessage调用WindowClient,所以 eventListener 还没有注册来监听该消息?那正确吗?

如何从服务人员和 postMessage 打开一个新窗口到该新窗口。

4

3 回答 3

2

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

我所做的是检查是否需要打开一个新的客户端窗口。如果我在 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,然后发回消息给服务人员向我们发送消息。

...
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我们的“全局”不是持久的,这很好,因为当推送通知到达时服务工作者“唤醒”时我们只需要它。

呈现的代码是代码,重点是解释这个想法,这对我有用。

于 2020-12-01T00:02:59.287 回答
-2
clients.openWindow(event.data.url).then(function(windowClient) {
        // do something with the windowClient.
      });
于 2018-07-18T05:03:19.920 回答
-3

我遇到了同样的问题。我的错误是我在窗口上注册了事件处理程序。但它应该像这样在 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 中检查。

于 2018-06-13T09:27:19.673 回答