首先,对于这个糟糕的标题,我深表歉意,但我做不到。
在 Chrome 44 中,添加了 Notfication.data 和 ServiceWorkerRegistration.getNotifications(),并在处理包含推送消息的通知时,扩展 / 简化了一些常见用例。
通知数据
借助 Notification.data,您可以将 JavaScript 对象与通知相关联。
这归根结底就是,当您收到推送消息时,可以创建包含一些数据的通知,然后在 notificationclick 事件中获取被点击的通知及其数据。
例如,创建一个数据对象并将其添加到通知选项中,如下所示:
self.addEventListener('push', function(event) {
console.log('Received a push message', event);
var title = 'Yay a message.';
var body = 'We have received a push message.';
var icon = '/images/icon-192x192.png';
var tag = 'simple-push-demo-notification-tag';
var data = {
doge: {
wow: 'such amaze notification data'
}
};
event.waitUntil(
self.registration.showNotification(title, {
body: body,
icon: icon,
tag: tag,
data: data
})
);
});
这意味着我们可以在 notificationclick 事件中获取相关信息:
self.addEventListener('notificationclick', function(event) {
var doge = event.notification.data.doge;
console.log(doge.wow);
});
在此之前,您必须将数据存储在 IndexDB 中,或在图标网址的末尾添加一些内容 - eek。
ServiceWorkerRegistration.getNotifications()
开发者在使用推送通知时,常常会要求更好地控制所显示的通知。
一个示例用例是聊天应用,在该应用中,用户发送多条消息,接收方显示多条通知。理想情况下,网络应用能够注意到您有多个未查看的通知,并将它们合并到一条通知中。
如果不使用 getNotifications(),您能做的最好的事情就是将上一个通知替换为最新消息。借助 getNotifications(),您可以在系统已显示通知时“收起”通知,从而提供更好的用户体验。
执行此操作的代码相对简单。在推送事件中,调用 ServiceWorkerRegistration.getNotifications() 以获取当前通知的数组,然后根据该数组确定正确的行为,无论是收起所有通知,还是使用 Notification.tag。
function showNotification(title, body, icon, data) {
var notificationOptions = {
body: body,
icon: icon ? icon : 'images/touch/chrome-touch-icon-192x192.png',
tag: 'simple-push-demo-notification',
data: data
};
self.registration.showNotification(title, notificationOptions);
return;
}
self.addEventListener('push', function(event) {
console.log('Received a push message', event);
// Since this is no payload data with the first version
// of Push notifications, here we'll grab some data from
// an API and use it to populate a notification
event.waitUntil(
fetch(API_ENDPOINT).then(function(response) {
if (response.status !== 200) {
console.log('Looks like there was a problem. Status Code: ' +
response.status);
// Throw an error so the promise is rejected and catch() is executed
throw new Error();
}
// Examine the text in the response
return response.json().then(function(data) {
var title = 'You have a new message';
var message = data.message;
var icon = 'images/notification-icon.png';
var notificationTag = 'chat-message';
var notificationFilter = {
tag: notificationTag
};
return self.registration.getNotifications(notificationFilter)
.then(function(notifications) {
if (notifications && notifications.length > 0) {
// Start with one to account for the new notification
// we are adding
var notificationCount = 1;
for (var i = 0; i < notifications.length; i++) {
var existingNotification = notifications[i];
if (existingNotification.data &&
existingNotification.data.notificationCount) {
notificationCount +=
existingNotification.data.notificationCount;
} else {
notificationCount++;
}
existingNotification.close();
}
message = 'You have ' + notificationCount +
' weather updates.';
notificationData.notificationCount = notificationCount;
}
return showNotification(title, message, icon, notificationData);
});
});
}).catch(function(err) {
console.error('Unable to retrieve data', err);
var title = 'An error occurred';
var message = 'We were unable to get the information for this ' +
'push message';
return showNotification(title, message);
})
);
});
self.addEventListener('notificationclick', function(event) {
console.log('On notification click: ', event);
if (Notification.prototype.hasOwnProperty('data')) {
console.log('Using Data');
var url = event.notification.data.url;
event.waitUntil(clients.openWindow(url));
} else {
event.waitUntil(getIdb().get(KEY_VALUE_STORE_NAME,
event.notification.tag).then(function(url) {
// At the moment you cannot open third party URL's, a simple trick
// is to redirect to the desired URL from a URL on your domain
var redirectUrl = '/redirect.html?redirect=' +
url;
return clients.openWindow(redirectUrl);
}));
}
});
关于此代码段,首先要强调的是,我们通过将过滤器对象传递给 getNotifications() 来过滤通知。这意味着,我们可以获取特定标记(在此示例中为特定对话)的通知列表。
var notificationFilter = {
tag: notificationTag
};
return self.registration.getNotifications(notificationFilter)
然后,我们会查看可见的通知,检查是否有与该通知关联的通知计数,并据此进行递增。这样一来,如果有一个通知告知用户有两条未读消息,那么当有新推送通知到达时,我们会指出有三条未读消息。
var notificationCount = 1;
for (var i = 0; i < notifications.length; i++) {
var existingNotification = notifications[i];
if (existingNotification.data && existingNotification.data.notificationCount) {
notificationCount += existingNotification.data.notificationCount;
} else {
notificationCount++;
}
existingNotification.close();
}
需要注意的一点是,您需要对通知调用 close()
,以确保从通知列表中移除通知。这是 Chrome 中的一个 bug,因为每个通知都会被下一个通知替换,因为使用的是相同的标记。目前,此替换未反映在 getNotifications()
返回的数组中。
这只是 getNotifications() 的一个示例,正如您所想,此 API 还可用于多种其他用例。
NotificationOptions.vibrate
从 Chrome 45 开始,您可以在创建通知时指定振动模式。在支持 Vibration API(目前仅限 Chrome for Android)的设备上,您可以自定义显示通知时使用的振动模式。
振动模式可以是数字数组,也可以是单个数字(被视为一个数字的数组)。数组中的值表示以毫秒为单位的时间,其中偶数索引 (0, 2, 4, ...) 表示振动的时长,奇数索引表示在下一次振动之前暂停的时长。
self.registration.showNotification('Buzz!', {
body: 'Bzzz bzzzz',
vibrate: [300, 100, 400] // Vibrate 300ms, pause 100ms, then vibrate 400ms
});
其余常见功能请求
开发者提出的另一个常见功能请求是能够在特定时间段后关闭通知,或者能够发送推送通知,目的只是在通知可见时关闭通知。
目前,您无法做到这一点,规范也不允许采用这种做法 :(但 Chrome 工程团队也知道这种使用情形。
Android 通知
在桌面设备上,您可以使用以下代码创建通知:
new Notification('Hello', {body: 'Yay!'});
由于平台限制,Android 从未支持此功能:具体而言,Chrome 不支持对 Notification 对象的回调,例如 onclick。但在桌面设备上,它用于显示您当前可能正在打开的 Web 应用的通知。
我之所以提及这一点,是因为最初,像下面这样简单的功能检测有助于您支持桌面设备,并且不会在 Android 上导致任何错误:
if (!'Notification' in window) {
// Notifications aren't supported
return;
}
不过,由于 Android 版 Chrome 现在支持推送通知,因此可以通过 ServiceWorker 创建通知,但不能通过网页创建通知,这意味着此功能检测不再适用。如果您尝试在 Android 版 Chrome 上创建通知,则会收到以下错误消息:
_Uncaught TypeError: Failed to construct 'Notification': Illegal constructor.
Use ServiceWorkerRegistration.showNotification() instead_
目前,针对 Android 和桌面设备进行功能检测的最佳方式是执行以下操作:
function isNewNotificationSupported() {
if (!window.Notification || !Notification.requestPermission)
return false;
if (Notification.permission == 'granted')
throw new Error('You must only call this \*before\* calling
Notification.requestPermission(), otherwise this feature detect would bug the
user with an actual notification!');
try {
new Notification('');
} catch (e) {
if (e.name == 'TypeError')
return false;
}
return true;
}
用法如下:
if (window.Notification && Notification.permission == 'granted') {
// We would only have prompted the user for permission if new
// Notification was supported (see below), so assume it is supported.
doStuffThatUsesNewNotification();
} else if (isNewNotificationSupported()) {
// new Notification is supported, so prompt the user for permission.
showOptInUIForNotifications();
}
的最佳实践