【发布时间】:2019-04-22 20:30:21
【问题描述】:
我一直在寻找如何让我的自定义推送通知正常工作的几个小时。以下是我设置项目的方式:没有前端框架、Node.js/Express.js 后端、作为推送管理器的 Firebase 云消息传递 (FCM) 和自定义服务工作者。我目前在 localhost 上托管应用程序,并且设置了 HTTPS 和一个 manifest.json,其中包含要开始的最少字段。 manifest.json 包含一个 start_url 字段,该字段指向 /index.html,即应用程序的登录页面。该应用程序与 webpack v. 4 捆绑在一起。
后端
在后端,我在特定路由器中设置了 Firebase Admin SDK,并向 FCM 服务器发送了一个类似于以下内容的自定义通知对象:
let fcMessage = {
data : {
title : 'Foo',
tag : 'url to view that opens bar.html'
}
};
当后端发生有趣的事件时,它会检索包含 FCM 注册令牌的用户列表并将通知发送到 FCM 服务器。这部分效果很好。
前端
我在前端有两个服务人员。在我的前端 index.js 中,我在域根目录中注册了一个名为 sw.js 的自定义服务工作者,并告诉 firebase 使用该服务工作者,如下所示:
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('./sw.js')
.then(registration => {
messaging.useServiceWorker(registration);
})
.catch(err => console.error(`OOps! ${err}`));
}
FCM 及其凭据已设置,用户可以订阅推送通知。我不会在这里展示该代码,因为它可以工作,而且我不认为这是问题所在。
现在谈谈服务人员本身。我的域根目录中有一个 firebase-messaging-sw.js 文件。它包含以下代码:
importScripts('https://www.gstatic.com/firebasejs/4.8.1/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/4.8.1/firebase-messaging.js');
firebase.initializeApp(configuration);
const messaging = firebase.messaging();
配置只是所有信用的占位符。再次,这些东西有效。
我想要做的是不使用 FCM 推送通知,而是创建我自己的推送通知,其中包含用户可以单击并转到该视图的视图的 URL。以下代码几乎可以工作,是我在另一个网站上找到的hack:
class CustomPushEvent extends Event {
constructor(data) {
super('push');
Object.assign(this, data);
this.custom = true;
}
}
self.addEventListener('push', (e) => {
console.log('[Service Worker] heard a push ', e);
// Skip if event is our own custom event
if (e.custom) return;
// Keep old event data to override
let oldData = e.data;
// Create a new event to dispatch
let newEvent = new CustomPushEvent({
data: {
json() {
let newData = oldData.json();
newData._notification = newData.notification;
delete newData.notification;
return newData;
},
},
waitUntil: e.waitUntil.bind(e),
})
// Stop event propagation
e.stopImmediatePropagation();
// Dispatch the new wrapped event
dispatchEvent(newEvent);
});
messaging.setBackgroundMessageHandler(function(payload) {
if (payload.hasOwnProperty('_notification')) {
return self.registration.showNotification(payload.data.title,
{
body : payload.data.text,
actions : [
{
action : `${payload.data.tag}`,
title : 'Go to link'
}
]
});
} else {
return;
}
});
self.addEventListener('notificationclick', function(e) {
console.log('CLICK');
e.notification.close();
e.waitUntil(clients.matchAll({ type : 'window' })
.then(function(clientList) {
console.log('client List ', clientList);
const cLng = clientList.length;
if (clientList.length > 0) {
for (let i = 0; i < cLng; i++) {
const client = clientList[i];
if (client.url === '/' && 'focus' in client) {
return client.focus();
}
}
} else {
console.log('no clients ', e.action);
clients.openWindow(e.action)
.then(client => {
console.log('client ', client);
return client.navigate(e.action);
})
.catch(err => console.error(`[Service Worker] cannot open client : ${err} `));
}
}))
});
该 hack 旨在捕获推送事件和 FCM 默认通知负载,而是通过通过 Notification API 制作的自定义负载来提供该负载。
上面的代码很好用,但前提是我把它放在 firebase-messaging-sw.js 文件中。这不是我真正想要做的:我想把它放在 sw.js 文件中,但是当我这样做时,sw.js 听不到任何推送事件,而是得到默认的 FCM 推送通知。我还尝试将整个 firebase-messaging-sw 脚本导入自定义服务工作者,但它仍然听不到消息事件。
为什么我想在我的 Service Worker 中使用它而不是在 Firebase 中使用它?它能够在传递到通知正文的“标签”字段的视图上打开应用程序。如果我使用 Firebase Service Worker,它会告诉我它不是活动的注册 Service Worker,虽然应用确实在新窗口中打开,但它只在 /index.html 上打开。
我做了一些小观察:当最后一段代码添加到 firebase-messaging-sw.js 文件时,clients 数组总是空的。自定义服务工作者安装正确,因为它处理应用程序外壳缓存并正常侦听所有其他事件。 firebase-messaging-sw service worker 也已正确安装。
【问题讨论】:
标签: javascript firebase push-notification firebase-cloud-messaging progressive-web-apps