原文转载于此,略有删减,本文根据知识共享署名4.0国际许可协议共享,BYTroland。本系列持续更新中,Github地址请查看这里。这是JavaScript工作原理的第9章。现在让我们将注意力转向Web推送通知:我们将研究它们的构造,探索发送/接收通知背后的过程,最后分享我们在SessionStack计划如何利用这些功能来创建新的产品功能。推送通知是移动设备上非常常见的功能。出于某种原因,网络上的推送通知已经存在了很长时间,尽管大多数开发人员都在大声疾呼。概述Web推送通知允许用户选择预定的时间从Web应用程序中获取及时的信息。它旨在为用户重新捕捉有趣、重要和及时的信息。推送服务是基于serviceworker线程的,在上一篇文章中有详细讲解。在这种情况下,使用服务工作线程是因为它将在后台运行,以免阻塞界面的渲染。对于推送通知,这非常重要,因为这意味着与推送通知相关的代码只会在用户与推送通知本身交互时执行。推送消息和通知推送消息和通知是两个不同的接口。消息推送-当消息推送服务器将消息推送到服务工作线程时调用。通知-Web应用程序中的服务工作者或脚本通过向用户显示通知来运行。消息推送消息推送的实现大致分为三步:接口——添加客户端逻辑,让用户订阅推送服务。在Web应用程序界面中编写JavaScript代码逻辑,以允许用户注册推送通知服务。发送消息——在服务器端实现接口调用,触发推送消息到用户设备。接收消息——在浏览器端接收到消息后立即处理推送消息。现在,让我们详细说明一下整个过程。兼容性检测首先需要检测当前浏览器是否支持消息推送服务。可以使用以下两个简单的检查:检查navigator对象上的serviceWorker属性和检查window对象上的PushManager属性。代码如下:if(!('serviceWorker'innavigator)){//当前浏览器不支持serverworkerthreads,禁用或隐藏接口返回;}if(!('PushManager'inwindow)){//当前浏览器不支持推送服务,禁用或隐藏接口return;}注册serviceworker线程现在支持消息推送功能。下一步是注册服务人员。通过前面的文章,您应该熟悉如何注册ServiceWorker。请求授权注册serviceworker线程之后,接下来就是进行用户订阅相关的操作。这需要用户授权才能向他们推送消息。授权接口相当简单,但缺点是接受参数的接口以前是回调函数,现在是Promise。因为无法获知当前浏览器支持的接口版本,所以需要进行兼容性处理。像这样:functionrequestPermission(){returnnewPromise(function(resolve,reject){constpermissionResult=Notification.requestPermission(function(result){//使用回调来处理已弃用的接口版本resolve(result);});if(permissionResult){permissionResult.then(resolve,reject);}}).then(function(permissionResult){if(permissionResult!=='granted'){thrownewError('Permissionnotgranted.');}});}调用Notification.requestPermission()会向用户弹出如下提示框:在获取、关闭或禁止权限时,可以得到granted、default或denied的结果字符串。需要注意的是,当用户点击Disable按钮后,Web应用程序不会再次向用户请求授权,直到用户手动启用更改授权状态。该选项隐藏在设置面板中。点击地址栏最左侧的信息按钮,弹出授权弹窗。通过PushManager订阅用户serviceworker线程注册授权成功后,在注册server线程时调用registration.pushManager.subscribe()即可订阅用户。整个代码片段如下(包括注册服务工作线程):真,applicationServerKey:btoa('BEl62iUYgUivxIkv69yViEuiBIa-Ib9-SkvMeAtA3LFgDzkrxZJjSgSnfckjBJuBkr3qBUYIHBQFLXYp5Nksh8U')};返回registration.pushManager.subscribe(subscribeOptions);}).then(function(pushSubscription:pushSubscription:PushSubscription:PushSubscription)));returnpushSubscription;});}registration.pushManager.subscribe(options)有一个options对象参数,其中包含必选或可选参数:userVisibleOnly:返回的推送订阅是否只对订阅者可见。必须设置为true否则会出现错误(这是历史原因)。applicationServerKey:包含公钥的Base64编码的DOMString字符串或ArrayBuffer,消息推送服务器使用公钥对应用服务器进行认证。消息推送服务器需要生成一对应用服务器密钥对——即VAPID密钥对,该密钥对是消息推送服务器唯一的。它们由一对公钥和私钥组成。私钥秘密存储在推送服务器上,公钥用于与客户端交换和通信。这些密钥允许推送服务识别订阅用户的应用服务器,并确保它是触发向给定用户推送消息的同一应用服务器。您只需要生成一次应用程序私钥/公钥对。您可以访问https://web-push-codelab.glit...生成密钥对。订阅用户时,浏览器将applicationServerKey(公钥)传递给推送服务,这意味着推送服务将应用服务器公钥绑定到用户的PushSubscription。流程如下:web应用加载完成后,调用subscribe,传入服务器公钥。浏览器向消息推送服务发起请求,生成端点信息,连同关键信息返回给浏览器。浏览器将对等信息添加到subscribe()承诺返回的PushSubscription对象中。之后,每当需要推送信息时,都必须发送一个认证头,其中包含由应用服务器的私钥签名的信息。每当推送服务收到推送消息的请求时,它会通过在传输头中查找已绑定到指定端的公钥(在第二步中)来验证它。PushSubscription对象PushSubscription包含向用户设备推送信息所需的所有信息。大概包含以下信息:{"endpoint":"https://domain.pushservice.com/some-id","keys":{"p256dh":"BIPUL12DLfytvTajnryr3PJdAgXS3HGMlLqndGcJGabyhHheJYlNGCeXl1dn18gSJ1WArAPIxr4gK0_dQds4yiQ=="FP端点为推送服务地址.当需要推送消息时,向该地址发起POST请求。keys对象包含用于加密与推送消息一起发送的消息数据的值。当用户订阅并返回PushSubscription对象时,需要将其保存在推送服务器上。这样订阅相关的数据就可以保存到数据库中,以后可以根据数据库中的存储值给指定的用户发送消息。消息推送当需要给用户发送消息时,首先需要一个消息推送服务。您通知推送服务(通过接口调用)要推送的数据、消息推送的目标用户以及在任何情况下如何发送消息。这些接口调用一般由消息推送服务器完成。消息推送服务消息推送服务用于接收消息推送请求、验证请求并将消息推送到指定的用户浏览器。请注意,此处的通知服务不受您控制——它是第三方服务。服务器只通过接口与消息推送服务通信。Google的FCM是推送消息服务之一。消息推送服务处理核心事务。例如,当浏览器处于离线状态时,推送服务会将消息排队并等待浏览器连接到Internet,然后再发送相应的消息。开发者可以选择让浏览器使用任何推送通知服务。但是所有的消息推送服务都有相同的接口,不会因为接口不同而增加消息推送的实现难度。处理消息推送的请求URL地址可以从PushSubscription对象的endpoint属性的值中获取。消息推送接口消息推送服务接口提供了向用户发送消息的方法。这个接口是一个IETF标准协议,叫做WebPushProtocol,它定义了如何调用消息推送服务。推送消息必须加密。这可以防止推送消息服务窥探发送的数据。这是至关重要的,因为客户端可以决定使用哪个消息服务(可能使用一些不受信任和不安全的消息服务)。消息推送参数:??TTL-定义消息在被删除且无法传输之前在队列中保留多长时间。Priority——定义每条消息的优先级,使消息推送服务只能推送高优先级的消息,方便用户节省设备电量。主题-设置推送消息的主题名称,以便将待处理的消息替换为相同的主题名称,这样一旦设备激活,用户就不会收到过期的消息。浏览器消息推送事件每当向上述推送服务发送消息时,消息将处于待处理状态,直到出现以下情况:设备连接到网络。队列中的消息停留时间超过设置的TTL。当消息推送服务向浏览器传输消息时,浏览器接收消息,对其进行解密,并将推送事件分派给服务工作者线程。这里的重点是即使网页没有打开,浏览器仍然可以执行serviceworker线程。发生以下事件:浏览器对收到的推送消息进行解密。浏览器唤醒serviceworker。服务工作者线程接收推送事件。监听推送事件与用JavaScript编写其他事件监听器非常相似。self.addEventListener('push',function(event){if(event.data){console.log('这个推送事件有数据:',event.data.text());}else{console.log('此推送事件没有数据。');}});关于ServiceWorker线程需要了解的一件事是它的运行时间无法由人控制。只有浏览器才能唤醒并结束它。在serviceworker中,event.waitUntil(promise)告诉浏览器serviceworker正在处理消息,直到promiseresolve,如果浏览器想要完成消息处理,它不应该中止serviceworker。以下是处理推送事件的示例:self.addEventListener('push',function(event){varpromise=self.registration.showNotification('Pushnotification!');event.waitUntil(promise);});称呼自己。registration.showNotification()向用户弹出一个通知,并返回一个承诺,一旦通知显示就解决。您可以使用可视化方法来设置满足您需要的showNotification(title,options)方法。标题参数是一个字符串,选项是一个对象,如下所示:{"//":"VisualOptions","body":"
