系列文章:

  1. Service Workers 和离线缓存

  2. Notification with Service Workers push events (本文)

  3. PWA:添加应用至桌面及分享

Notification

HTML5 Notification 已经推出挺久了,它可以用来给用户发送通知提示。

一直想试一试给自己的博客用上这个功能。上一篇成功升级 https 之后,终于可以来捣鼓一下了。捣鼓之前,还是先来看一下浏览器支持情况。

Notification 浏览器支持情况

从上图中可以看到,除了我行我素的 IE 之外,其他桌面浏览器都已经支持 Notification;与之相反,移动端一片血红,几乎全军覆没。自己玩就不用在意这些了,而且 Notification 已加入标准,移动端浏览器最终也会响应号召的?。

So, JUST DO IT.

虽然,桌面浏览器已经基本支持 Notification,但 Notification 之中还有很多配置项。之前,有看到过大神写的一篇关于 Notification 的文章,上面列举了 Notification 的属性,比如,sound, vibrate, image 等。于是,上 MDN 看了下它们的支持情况,

同样也是一大片血红,普遍也就只支持最基础的功能。

小试牛刀

想要尝试 Notification 非常方便,打开浏览器的 console 就可以了。

首先,申请推送的权限,在 console 中输入

Notification.requestPermission();

就可以看到浏览器左上角弹出提示问你是否允许推送。

权限有 3 种状态:granted(同意), denied(拒绝)和 default,默认是 default。默认权限浏览器行为和拒绝相同,不会发起推送,只有在获得用户同意后,浏览器才会发起推送。

requestPermission 会返回一个 Promise,当用户选择后,会将用户所做的决定(即granted, denied, default)作为参数传递给 then 方法。

获得了用户同意的授权之后,就可以发起推送了。发起推送也很简单,只需创建一个 Notification 对象。

// new Notification(title[, options]);
new Notification('Hello world.');

通常情况下,这时你就能看到屏幕右上角会弹出个小框。不过总会遇到一些特例:mac 下 chrome 满屏状态下 Notification 不会实时弹出,只有切换到桌面状态下才可能弹出。然而,它就像个磨人的小妖精,你不知道它会在什么时候弹出,可能是下一秒,可能是一个小时以后,也可能是明天...(firefox 和 safiri 满屏下没有这个问题)

其他具体的一些 API 就不细讲了,有兴趣的可以看 MDN 的文档,或者之前提到的那篇文章。

试过了最基本的 Hello world,那么,再进一步试着搞到项目中看看。

当今,人们都睡得比较晚,有的是工作原因,有的是因为有晚睡强迫症,也有时是专注于什么一下子忘了时间。

这时就可以用 Notification 来做个提示,提醒自己早点休息。

const NOTIFICATION_API = 'Notification';
const PERMISSION_GRANTED = 'granted';
const NOTIFICATION_START_TIME = 23;
const NOTIFICATION_END_TIME = 6;
const DELAY_MINUTES = 5;
const NOTIFICATION = {title: '夜深了',delay: DELAY_MINUTES * 60 * 1000, // 5 minutesoptions: {body: '亲,工作之余,也要注意身体噢...',icon: '/favicon.ico'}
};const isSupportNotification = () => NOTIFICATION_API in window;
const getPermission = () => Notification.permission;
const isPermissionGranted = permission => permission === PERMISSION_GRANTED;const registerNotification = () => {const now = new Date();const nowHour = now.getHours();// Time in the notification time blockif (nowHour <= NOTIFICATION_END_TIME || nowHour >= NOTIFICATION_START_TIME) {// Show notification 5 minutes latersetTimeout(() => new Notification(NOTIFICATION.title, NOTIFICATION.options), NOTIFICATION.delay);} else {// Show notification at 11 o'clock.const start = new Date(now.getFullYear(), now.getMonth(), now.getDate(), NOTIFICATION_START_TIME, DELAY_MINUTES);setTimeout(() => new Notification(NOTIFICATION.title, NOTIFICATION.options), start.valueOf() - now.valueOf());}
};if (isSupportNotification()) {if (isPermissionGranted(getPermission())) {registerNotification();} else {Notification.requestPermission().then(isPermissionGranted).then(granted => granted && registerNotification());}
} else {console.info('Browser not support Notification.');
}

有兴趣的话,你还可以多捣鼓几个。但是,这些提示说起来都是程序写死的,当页面加载之后就决定了它显示的时间,而不是动态产生的。如果,想要发送动态提示,这就需要客户端与服务器端的配合,还是先来看客户端。

通过 service workers push events 来接收消息

上一篇中已经成功地在客户端注册了 service workers,通过它来获取服务端发送的消息就很简单了。

监听 Service workers 中的 push 事件,就能获取来自推送服务器的消息,再通过 registration.showNotification 方法就能发出 Notification 了。

// service-worker.js
// ...
const onPush = function(event) {event.waitUntil(_self.registration.showNotification('New Post Arrival', {icon: '/logo.png'}));
};_self.addEventListener('push', onPush);

现在就可以打开 firefox 试一试了,打开 service workers 调试页,点击推送就可以预览效果了。(为什么不用 chrome?这个问题后面会说...)

是不是以为这样就完成了?那就错了,这才刚刚完成了一半,服务器怎么知道是给你发推送,而不是隔壁老王?

客户端订阅

这就需要客户端将自己与其他客户端区分的信息告诉服务器,而这个信息就是订阅信息,在 service workers 注册时可以拿到。我们再修改一下之前的代码...

// ServiceWorkerService.js
// ...
sw.register(SERVICE_WORKER_FILE_PATH).catch(() => console.error('Load service worker fail')).then(registration =>registration.pushManager.getSubscription().then(subscription => subscription || registration.pushManager.subscribe({ userVisibleOnly: true }))).then(subscription => {const endpoint = subscription.endpoint;const options = {method: 'post',headers: {'Content-Type': 'application/json'},body: JSON.stringify({ endpoint })};return httpFetch(SUBSCRIBE_API, options);}).catch(error => console.error('Subscribe Failure: ', error.message)).then(() => sendMessageToSW('Hello, service worker.')).catch(() => console.error('Send message error.'));

在注册 service worker 时,先通过 pushManager.getSubscription 方法获取当前客户端是否已经订阅过了,没有订阅则通过 pushManager.subsribe 方法来获取一个订阅;接着就将订阅信息发送给后端,交由后端储存起来,服务端的接口这里就不贴了,有兴趣的看 Github 上的代码吧。

订阅信息是最重要的资料,需要妥善保存,一旦泄露别人就能轻易冒充你了。

订阅信息会过期,所以不要忘了在 servier worker 中监听 pushsubscriptionchange 事件,当订阅过期后自动重新订阅。

拿到了订阅信息,接着就可以来推送消息了。不过得先说明一点,这里所说的服务器推送与 http2 的 server push 没有任何关系(虽然,之前我一直是这么认为的...彡(-_-;)彡)。

打个岔

说到 http2,就顺便说一个 nginx 升级 http2 时遇到的问题。ubuntu 14.04 下需要将 OpenSSL 升级至 1.0.2,nginx 才能开启 http2。

Note that accepting HTTP/2 connections over TLS requires the “Application-Layer Protocol Negotiation” (ALPN) TLS extension support, which is available only since OpenSSL version 1.0.2. Using the “Next Protocol Negotiation” (NPN) TLS extension for this purpose (available since OpenSSL version 1.0.1) is not guaranteed.

但如果,和我一样使用 nginx docker 镜像的话,使用 alpine 版本就能开启 http2,而不必操心上面所提的了。

服务器推送

言归正传,这里的服务器推送是基于发布/订阅模式构成的一套体系,通过客户端的订阅行为向服务器注册,当服务器广播消息时,将消息传递给推送服务,再由推送服务器给客户端推送消息。

你可能会像我一样纳闷,推送服务是什么鬼?自己的服务器支持 http2,可以 server push 那是不是可以直接推送消息,而不通过推送服务哪?答案是,No way。这里的推送服务(Push Service)指的是 google 的 fcm (以前叫 gcm),或者 apple 的 APNs(苹果现在还不支持 webpush)等。这点可以从上面 firefox 截图中的推送服务后的字符串看出端倪 https://updates.push.services...,同时,它也是客户端提交给服务器的订阅信息。

知道了这些就能理解规范上的 webpush 架构了。

要发送通知时,服务器端取出之前客户端上传的订阅信息,即刚提到的 url 地址,往这个地址发一个 post 请求就可以了,剩下的事推送服务会替你完成。

// publish.js
// ....post('/broadcast', async ctx => {await readEndpoints().then(endpoints => {ctx.status = 200;ctx.body = {};endpoints.forEach(endpoint => {webPush.sendNotification({ endpoint }).catch(console.error);});}).catch(err => {ctx.status = 500;ctx.body = err;});});

web-push & payload

给客户端发送推送内容时,需要对推送内容进行加密,这里使用了 web-push 这个库来帮加密内容,并将消息传递给推送服务器。实现了推送服务后,就不用再通过控制台去模拟推送服务了。

上面是最基础的用法,如果要带上 message,就需要在客户端注册时向后端传递 p256dhauth。服务器发送消息时,通过这两个值来给 message 加密,当然加密的过程都交给 web-push 来做。通过 Postman 发个消息?

同之前测试一样,在系统右上角弹出了提示,通常通知都可以被点击,这点 web notification 也可以做到...

响应点击事件

在 service workers 中可以监听 notification 的 click 事件,再通过 clients 操作,就能达到一些诸如打开一个新页面等类似的效果。

// service-worker.js
// ...
const onNotificationClick = function(event) {event.notification.close();event.waitUntil(clients.openWindow(event.notification.data));
};_self.addEventListener('notificationclick', onNotificationClick);

现在点击推送,就会打开我的网站啦~?

可惜的是,当浏览器关闭时,推送就接收不到了。

搞完了 firefox,但也不能忘了老朋友 chrome,之前有提到现在这套代码在 chrome 下无法成功订阅,如果想要 chrome 支持,那么还得用上古哥服务。

配置古哥服务

重要提示:使用 google 服务需要科学上网...

想要 chrome 下 service workers 能够发出 Notification 并不复杂,只需以下几步:

  1. 由于,GCM 已经被 FCM(Firebase Cloud Messaging) 替代,所以,先要先开通 firebase

  2. 创建一个项目

  3. 查看 setting(⚙)中的 cloud messaging 信息(Server keySender ID)

  4. 客户端根目录下添加 manifest.json,并设置 gcm_sender_idapplicationServerKey,分别对应项目的Sender IDServer key

  5. 服务器端,在使用 web-push 调用 sendNotification API 时添加 gcmAPIKey(填 Server key

通过这几步,chrome 就和 firefox 一样可以接受通知消息了。因为,项目之前没有使用 Firebase,所以,个人没有直接使用它所提供的 API 来发送通知。如果,你的项目中已经用到了 Firebase,那么,你可以根据手册直接使用 firebase 封装后的 API 来接收消息,那样可能会更简单一点。

配置成功之后,就可以试试 Notification 在移动端 chrome 下的效果。

服务器发出消息后,notification 就会出现在系统的消息提示栏里,点击通知也会打开新的页面。(和桌面端一样,浏览器彻底关闭后就无法接收到消息了)

是不是很酷~

再次提示:使用 google 服务需要科学上网...

(google 翻译摊手竟然是 tanshou...?)

最后

至此,从客户端订阅,到服务器发送推送消息,再到客户端接收推送消息一整套的功能就完成了。尽管,无论是桌面端还是移动端在浏览器关闭的情况下,Service Workers 都无法接收到推送消息,但这个功能还是能够极大得增加用户的粘性,尤其在桌面端(大多情况都会打开着浏览器)。

Tips: 开发时,记得勾选 Application -> Service Workers 下的 Update on reloadBypass for network,这样 service worker 的更新会被立即应用。

同时,推荐 mozilla 的 Service Workers Cookbook 真的很棒!论文档、Demo,M 家优势明显。

如果,你喜欢我的文章,欢迎来我的博客并开启通知,这样每当有新的文章,你就会第一时间收到通知啦~相信有些小伙伴已经收到了?

有了 SW 和 Notification 还要啥 R(自)S(行)S(车)...[手动滑稽]

内容如有不妥之处,请指出,谢谢...

Notification with Service Workers push events相关推荐

  1. [译] Service workers:PWA应用背后的英雄

    写在前面 作者:Flavio Copes 原文地址:Service workers: the little heroes behind Progressive Web Apps 说明:本人水平有限,在 ...

  2. html5的service worker,GitHub - w3c/ServiceWorker: Service Workers

    What's going on here? Service workers are a new browser feature that provide event-driven scripts th ...

  3. 前端开发的workers——web workers、share workers和service workers

    文章目录 突破单线程的瓶颈WEB WORKERS 兼容性 私有worker "杀死"workers 错误调试 异步在worker的表现 共享的shareWorkers servic ...

  4. 深入浅析Service Workers

    本篇文章给大家介绍一下JavaScript API - Service Workers.有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助. 相关推荐:<编程入门> servi ...

  5. Service Workers让网站动态加载Webp图片

    Service Workers加载webp图片 每个图片加载请求可以通过accept获取是否支持webp格式,例如如下图 利用这一点,我们可以判断支持webp图片,就使用webp图片.我们需要注册一个 ...

  6. 【苹果推位置推iMessage】Apple Notification Center Service (ANCS)

    推荐内容IMESSGAE相关 作者推荐内容 iMessage苹果推软件 *** 点击即可查看作者要求内容信息 作者推荐内容 1.家庭推内容 *** 点击即可查看作者要求内容信息 作者推荐内容 2.相册 ...

  7. 【android】Notification 和 Service的结合应用以及Notification在Android8.0之后的坑 Bad notification

    在结合<第一行代码>复习Service时踩到一个坑--notification 弹不出来,然后就crush掉了!!! 查阅相关资料发现是在Android8之后,notification很多 ...

  8. PWA:添加应用至桌面及分享

    系列文章: Service Workers 和离线缓存 Notification with Service Workers push events PWA:添加应用至桌面及分享(本文) 继上两篇离线缓 ...

  9. 马宁的Windows Phone 7.1初体验(二)——Push Notification

    Push Notification并不是Windows Phone 7.1的新功能,但是之前的文章里对这部分都缺少详细的分析,所以姑且就把Push Notification放到这部分里吧. 很多iOS ...

最新文章

  1. mysql事务隔离级别 花_MySQL事务的隔离级别
  2. java 状态设计模式_JAVA设计模式:状态模式
  3. 『设计模式』简单工厂模式
  4. 加载页面就触发ajax,AJAX post方法,有时会在页面加载时触发,有时不会
  5. php上js实现ajax请求,原生JS如何实现Ajax通过POST方式与PHP进行交互的方法
  6. spyder python调试查看类信息_使用Spyder进行Python调试
  7. 数据集可视化——tile(贴砖)
  8. 行如风 Angular初识
  9. 学习单片机c语言必备的两个软件,单片机C语言应用100例(第2版)
  10. 【SDCC 2016】微影时代、普元、亚信、Fit2Cloud、VMware、京东商城、优维科技畅聊自动化运维与容器...
  11. linux的fseek函数
  12. 浅谈网络安全之内存取证
  13. linux查看nbu数据库命令,NBU基本常用命令
  14. python音频加速_python将音频进行变速的操作方法
  15. 普华永道高级JAVA面试记录
  16. 存在即合理-开发语言
  17. 计算机拼歌曲,抖音你这辈子有没有为别人拼过命是什么歌
  18. 区块链成热点赛道,云技术如何赋能结合?
  19. 华为公有云认证培训认证体系- HCIA,HCIP ,HCIE
  20. Python and运算符

热门文章

  1. 【闲聊产品】之五:谁来背黑锅?
  2. 【cocos2d-x】游戏构成要素②----使用多个层
  3. 关于spring的p标签(转)
  4. 部署exchange邮件系统的边缘服务器
  5. TTIC Postdoc Position
  6. 初学Portal的基本概念
  7. 为什么开发中逐渐抛弃jsp(转)
  8. 内存对齐与ANSI C中struct型数据的内存布局 【转】
  9. windows下编译boost
  10. zabbix监控之nginx状态监控(一)