前言
接上篇步步提高网站体验系列之——PWA(Manifest)。这次通过消息通知Notification,当CSDN博客有更新时,用户进入个人网站会收到一个提醒。
首先要明确一点,消息通知(Notification)不是推送(Push)。与通知相比,PWA的推送机制有更复杂的依赖与更差的兼容性,当然效果也更加的牛逼。
本文只讲消息通知,推送等实现好了再发文
文章目录如下:
Notification:
简介
它允许服务器向用户提示一些信息,并根据用户不同的行为进行一些简单的处理。
通知比较常见的使用情景包括电商网站提醒用户一些关注商品的价格变化,或是在线聊天网站提醒用户收到了新消息等等。我的场景就是有新文章发布了用户可以收到提醒。
兼容性
chrome是完全兼容的,Mac safari也支持,ios safari完全不支持。
就Mac safari多说一点:
在chrome中,调用Notification是这样的:在sw.js和业务代码中的调用方式是一样的,即通过 ServiceWorkerRegistration
return navigator.serviceWorker.getRegistration().then(registration => {
registration && registration.showNotification(text, options)
})
在Mac safari中,调用Notification是这样的:并且在sw.js上下文中没法用,在业务代码中可以用。
在safari中可以通过postMessage将消息传递给客户端,在客户端调用Notification
new Notification(text, options)
依赖
依赖就是PWA的依赖,https,注册service worker。
实现
1. 注册Notification
由于逻辑越来越复杂,将这册service worker以及notification的脚本index.html中移出,在vue app构建时的beforeCreate中调用:
import register from './pwa/register.js'
let app = new Vue({
el: '#app',
router,
components: { App },
template: ' ',
beforeCreate: function () {
register.registePWA()
}
})
再看register.js中Notification部分
// 注册service worker
....
// 注册Notification
function registeNotification () {
if (!('Notification' in window)) {
// Push isn't supported on this browser, disable or hide UI.
return
}
new Promise((resolve, reject) => {
// 询问用户是否允许网站推送通知
const permissionPromise = Notification.requestPermission(result => {
resolve(result)
})
if (permissionPromise) {
permissionPromise.then(resolve)
}
}).then(result => {
if (result === 'granted') {
console.log('Notification registration successful')
} else {
console.log('Notification: no permission')
}
})
}
export default {
registePWA () {
registeServiceWorker()
registeNotification()
}
}
与Push不同,Notification不是通过server端向所有监听订阅事件的浏览器推送,而只是浏览器自己发起的通知。
站点的需求如果csdn上我有新博客上线,用户再次打开网站时会得到一个通知。实现思路是这样的:依然利用service worker的离线缓存能力,对于getList请求,对比网络返回结果和上次缓存结果,如果网络返回结果更多,说明有新文章上线。
我这一版实现比较简单,只是单纯比较数组长度,忽略了很多场景,但是思路没有什么问题,可以在此基础上继续完善。
// action即为用户在提醒框的自定义命令
const options = {
// body: '',
icon: '../../../static/avatar.png',
actions: [{
action: 'csdn',
title: '去看看'
}],
tag: 'csdn-site',
renotify: true
}
this.addEventListener('fetch', function (event) {
event.respondWith(caches.match(event.request).then(function (response) {
let isGetListRequest = event.request.url.indexOf('getList') > -1
// ...
try {
if (isGetListRequest) {
Promise.all([httpRes.clone().json(), response.clone().json()]).then((jsonArry) => {
let httpResJSON = jsonArry[0]
let resJSON = jsonArry[1]
if (httpResJSON.length > resJSON.length) {
self.registration.showNotification('有新文章发布,去看看吧', options)
}
})
}
} catch (e) {
console.warn('the browser not support nitification')
}
//...
})
}
// 监听提醒框自定义action事件
this.addEventListener('notificationclick', function (e) {
var action = e.action
e.notification.close()
e.waitUntil(
// 获取所有clients
this.clients.matchAll().then(function (clients) {
if (!clients || clients.length === 0) {
this.clients.openWindow && this.clients.openWindow('https://blog.csdn.net/Napoleonxxx')
return
}
clients[0].focus && clients[0].focus()
clients.forEach(function (client) {
// 使用postMessage进行通信,使得客户端也可以响应action
client.postMessage(action)
})
})
)
})
运行结果