前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >h5的Notification 、web Push介绍

h5的Notification 、web Push介绍

作者头像
wo.
发布2021-06-15 11:20:00
4.1K0
发布2021-06-15 11:20:00
举报
文章被收录于专栏:了不得的专栏了不得的专栏
前言

本文是客户端和服务器端通信最后一个系列,主要介绍h5的桌面通知和web push,h5的Notification主要用于向用户展示通知,而web push 主要用于订阅推送消息。

h5的Notification

关于h5的Notification已经不是什么新的技术,但是最新chrome浏览器也是只有在https协议下面才有效。下面简单介绍一下Notification

语法
代码语言:javascript
复制
let myNotification = new Notification(title, options);
参数

一、 title 定义一个通知的标题,当它被触发时,它将显示在通知窗口的顶部。

二、 options 可选 options对象包含应用于通知的任何自定义设置选项。选项有:

代码语言:javascript
复制
dir: 显示通知的方向。默认是auto,跟随浏览器语言设置行为,你也可以通过设置ltr和rtl的值来覆盖该行为(虽然大多数浏览器似乎忽略这些设置)
lang: 通知的语言,如使用代表一个BCP 47语言标签的  DOMString 指定的。请参阅Sitepoint ISO 2字母语言代码页面,以获得简单的参考。
badge: 一个 USVString 包含用于表示通知的图像的URL, 当没有足够的空间来显示通知本身时。
body: 一个 DOMString 表示通知的正文,将显示在标题下方。
tag:  一个 DOMString 代表通知的 一个识别标签。
icon:  一个 USVString 包含要在通知中显示的图标的URL。
image: 一个 USVSTring包含要在通知中显示的图像的URL。
data: 您想要与通知相关联的任意数据。这可以是任何数据类型。
vibrate: 一个振动模式 vibration pattern 设备的振动硬件在通知触发时发出。
renotify: 一个 Boolean 指定在新通知替换旧通知后是否应通知用户。默认值为false,这意味着它们不会被通知。
requireInteraction: 表示通知应保持有效,直到用户点击或关闭它,而不是自动关闭。默认值为false。

以下选项列在最新规范中,但是目前浏览器可能尚未支持,大家可以随时尝试!

代码语言:javascript
复制
silent: 一个 Boolean 指明通知是否应该是无声的,即,不需要发出声音或振动,无论设备设置如何。默认值为false,这意味着它不会保持静默。
sound:一个 USVString 包含通知触发时要播放的音频文件的URL。
noscreen: 一个 Boolean 指定通知触发是否应启用设备的屏幕。 默认值为false,这意味着它将启用屏幕。
sticky: 一个 Boolean 指明通知是否应该是“粘”, 即不易被用户清理。默认值为false,这意味着它不会粘。

案例

代码语言:javascript
复制
function spawnNotification(theBody,theIcon,theTitle) {
  var options = {
      body: theBody,
      icon: theIcon
  }
  var n = new Notification(theTitle,options);
}

web push

用户订阅了一个站点的 Web Push 服务后,即使用户关闭了浏览器,一旦站点主动发送推送消息,用户都能收到,只要你的电脑是开着的。这是目前谷歌和苹果在 Chrome 和 Safari 上都力推的一种全新推送服务,Firefox最近也加入了这个阵营。

web push 优点

一、 可以完美替代原来的Email订阅服务,因为 Email 订阅这个动作要用户主动发起,不管你在页面内放了多显眼的订阅标志,都得用户去主动点击,填写自己 Email 地址才行。而且,如果不使用邮件客户端,很多订阅的 Email 发出去犹如石沉大海,到达率很低。

Web Push 完全由浏览器开发商的标准协议发起,一旦用户访问开通了 Web Push 的站点 ,浏览器就会主动询问你是否要订阅,弹出的订阅框也都是浏览器开发者的标准规范。首先,它增加了用户订阅的可能性。

二、 一旦用户点阅,只要你打开电脑,就会收到推送通知,没有邮件订阅时用户主动去查收邮件的问题,大大增加了订阅推送到达率。

三、 Web Push 让站点为主,APP 为辅的网站可以摆脱 APP 安装量少的困扰,访问你站点的人,只有一小部分会安装你的 APP,而Web Push 不需要安装任何 APP 在电脑上,只要用户点击一次Allow来订阅消息即可。

web push 国内使用少的原因

1、 Web Push 使用了 GCM 服务,而 GCM 在国内基本上很难连通,国内用户除非翻了,否则压根不可能订阅成功 Chrome 的 Web Push。

2、 谷歌要求 Chrome 的 Web Push 必须用 https 加密传输,国内目前很多网站还在使用http协议。 所以目前国内web push 使用较少。

web push 流程

1、客户端完成请求订阅一个用户的逻辑 2、服务端调用遵从web push协议的接口,传送消息推送(push message)到推送服务器(该服务器由浏览器决定,开发者所能做的只有控制发送的数据) 3、推送服务器将该消息推送至对应的浏览器,用户收到该推送

第一步, 客户端请求订阅用户,过程如下:

说明一下这三步,在第一步之前,应用服务器需要生成应用服务器密钥(application server keys),其作用是标识该服务器,保证每次发消息推送的都是同一个服务器。然后,客户端将会请求用户授权消息推送,一旦用户授权,浏览器就会生成一个PushScription,然后这个PushScription将会被发送至服务器,存入数据库,在后面的消息推送中使用。

第二步, 应用服务器发送web push协议标准的api,触发推送服务器的消息推送,其中headers必须配置正确,且传送的数据必须是比特流。

应用服务器发送消息推送请求(目的是为了将更新推送到用户的浏览器),为了向推送服务器发出请求,需要查看先前获得的PushScription,取出其中的endpoint,即为推送服务器配置给该用户的访问点。

一个PushScription对象如下:

代码语言:javascript
复制
{
  "endpoint": "https://random-push-service.com/some-kind-of-unique-id-1234/v2/",
  "keys": {
    "p256dh" :
"BNcRdreALRFXTkOOUHK1EtK2wtaz5Ry4YfYCA_0QTpQtUbVlUls0VJXg7A8u-Ts1XbjhazAkj7I99e8QcYP7DkM=",
    "auth"   : "tBHItJI5svbpez7KI4CCXg=="
  }
}

其中的endpoint包含了推送服务器域名,path后面的部分为推送服务器为每个用户分配的一个标识符。

发送数据时,数据必须编码(出于安全性考虑)。推送服务器在接收到这样一个请求之后,立即开始监听用户浏览器是否处于在线状态,若是,则将消息推送发送至浏览器。

第三步, 浏览器端接收消息推送,触发push事件并展示

浏览器在接收到推送服务器发来的推送后,将其解码并触发一个push事件。Service Worker由于它可以在浏览器页面未打开,浏览器未打开时执行,因此一般选择它完成web push的最后一步,即响应push事件完成展示通知等业务逻辑。


web push实现细节

1、 按照上一部分所说,首先进行用户订阅。 首先注册一个Service Worker,若注册成功,返回的Promise为resolve状态,如下:

代码语言:javascript
复制
function registerServiceWorker() {
  return navigator.serviceWorker.register('service-worker.js')
  .then(function(registration) {
    console.log('Service worker successfully registered.');
    return registration;
  })
  .catch(function(err) {
    console.error('Unable to register service worker.', err);
  });
}

(关于Service Worker,这方面的详细文章,大家可以自行网上搜索。)

2、 随后测试window环境下是否有Notification对象(此处以chrome为例,若使用firefox,uc等浏览器,需要遵循其相应标准,调用对应对象方法或引入JS SDK包), 测试成功,调用Notification.requestPermission请求用户授权发送推送,若授权成功,将会返回'granted'。

3、 接下来要做的就是使用注册好的Service Worker对象,调用pushManager.subscribe方法,从客户端获得刚刚所说的PushScription对象。

代码语言:javascript
复制
function subscribeUserToPush() {
  return navigator.serviceWorker.register('service-worker.js')
  .then(function(registration) {
    const subscribeOptions = {
      userVisibleOnly: true,
      applicationServerKey: urlBase64ToUint8Array(
        'BEl62iUYgUivxIkv69yViEuiBIa-Ib9-SkvMeAtA3LFgDzkrxZJjSgSnfckjBJuBkr3qBUYIHBQFLXYp5Nksh8U'
      )
    };

    return registration.pushManager.subscribe(subscribeOptions);
  })
  .then(function(pushSubscription) {
    console.log('Received PushSubscription: ', JSON.stringify(pushSubscription));
    return pushSubscription;
  });
}
解释:
代码语言:javascript
复制
1、userVisibleOnly是为了保证推送对用户可见,

2、application server key则如前文所说,是推送服务器用以识别应用服务器的密钥,这里的密钥包含了公钥和私钥,传输的是公钥。

3、同时,PushScription的endpoint也是在这个过程中生成的,生成公钥和私钥可以使用web-push库。

这里再次说明一下推送服务器的不可选择性,在调用subscribe生成PushScription时,浏览器会向它指定的中转服务器发送请求来生成endpoint和其余部分,这是没法控制的。

4、PushScription中的auth和p256dh是用来控制带载荷的push message的。

4、 获取到PushScription对象后,将其发往应用服务器,此处简化了存储,使用nedb存下PushScription并返回Promise:

代码语言:javascript
复制
function saveSubscriptionToDatabase(subscription) {
  return new Promise(function(resolve, reject) {
    db.insert(subscription, function(err, newDoc) {
      if (err) {
        reject(err);
        return;
      }
      resolve(newDoc._id);
    });
  });
};

5、 存储完毕后,接下来就是开发后台管理逻辑,使得管理员能够触发向用户推送消息的事件,应用服务器所做的逻辑就是遍历在数据库中存储的所有PushScription并推送消息,以下是使用web-push库完成配置密钥及联系邮箱的示例:

代码语言:javascript
复制
const vapidKeys = {
  publicKey:
'BEl62iUYgUivxIkv69yViEuiBIa-Ib9-SkvMeAtA3LFgDzkrxZJjSgSnfckjBJuBkr3qBUYIHBQFLXYp5Nksh8U',
  privateKey: 'UUxI4O8-FbRouAevSmBQ6o18hgE4nSG3qwvJTfKc-ls'
};

webpush.setVapidDetails(
  'mailto:web-pust-test@liaobu.de',
  vapidKeys.publicKey,
  vapidKeys.privateKey
);

6、 不要忘了配置你在谷歌云服务(例如FCM)申请到的GCMApiKey:

代码语言:javascript
复制
webpush.setGCMAPIKey('<Your GCM API Key Here>');

7、 配置完成后,就可以将subscription发送出去,使用web-push的sendNotification接口:

代码语言:javascript
复制
webpush.sendNotification(pushSubscription, 'Your Push Payload Text');

推送服务器发送消息后,会触发浏览器的push事件,为了控制service worker的逻辑,需要使用event.waitUntil方法,此方法接收一个promise参数,在promise变为resolved状态后,浏览器就会检查通知是否已被展示,若是,则关闭service worker。

如果不处理未正常执行的promise,部分浏览器如chrome会展示默认消息框:

展示一个通知调用的为showNotification方法,传的参数包括title等,如下:

代码语言:javascript
复制
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';

event.waitUntil(
    self.registration.showNotification(title, {
      body: body,
      icon: icon,
      tag: tag
    })
);

而展示notification时,除了控制它的视图层以外,也可以控制它的逻辑层, 例如点击消息通知后进行某些操作等等,在先前调用showNotification时可以传入一些参数, 例如,根据不同的action执行不同的操作:

代码语言:javascript
复制
self.addEventListener('notificationclick', function(event) {
  if (!event.action) {
    // Was a normal notification click
    console.log('Notification Click.');
    return;
  }

  switch (event.action) {
    case 'coffee-action':
      console.log('User ❤️️\'s coffee.');
      break;
    case 'doughnut-action':
      console.log('User ❤️️\'s doughnuts.');
      break;
    case 'gramophone-action':
      console.log('User ❤️️\'s music.');
      break;
    case 'atom-action':
      console.log('User ❤️️\'s science.');
      break;
    default:
      console.log(`Unknown action clicked: '${event.action}'`);
      break;
  }
});
web-push请参见github: https://github.com/web-push-libs/web-push
兼容性及其他问题

与ajax轮询、http长连接、WebSocket的对比

  • ajax轮询是通过客户端不断向服务端发送http请求,若有新消息就取回的模式保持数据实时更新,但这种方式需要服务器有很快的处理速度和资源
  • http长连接是客户端向服务器发送请求后,若服务器没有新数据要发送,就不返回response,一旦有了新数据返回了response,客户端就立刻再发一个request,周而复始。事实上这是把http协议的不对称性从客户端转移到了服务端
  • WebSocket是HTML5中提出的一个新标准(也可视之为协议),客户端在发送请求时在请求头加入额外的字段,以标识这是一个基于WebSocket协议的连接,服务器根据这个请求头生成响应,与客户端建立起WebSocket连接,之后服务端有新消息时,直接向客户端推送即可

不同浏览器兼容性

  • chrome采用的推送服务器为gcm或fcm,firefox也有自己的推送服务器
  • uc前些时间构建了自己的推送服务器,引入其官网上的sdk包,申请使用后即可用于开发
这就是整一个过程,具体来说:
(1)浏览器发起询问,生成subscription

在注册完service worker后,调用subscribe询问用户是否允许接收通知,如下代码所示:

代码语言:javascript
复制
navigator.serviceWorker.register("sw-4.js").then(function(reg){
    console.log("Yes, it did register service worker.");
    if (window.PushManager) {
        reg.pushManager.getSubscription().then(subscription => {
            // 如果用户没有订阅
            if (!subscription) {
                subscribeUser(reg);
            } else {
                console.log("You have subscribed our notification");
            }       
        });     
    }
}).catch(function(err) {
    console.log("No it didn't. This happened: ", err)
});

上面代码在发起订阅前先看一下之前已经有没有订阅过了,如果没有的话再发起订阅。

发起订阅的subscribeUser实现如下代码所示:

代码语言:javascript
复制
function subscribeUser(swRegistration) {
    const applicationServerPublicKey = "BBlY_5OeDkp2zl_Hx9jFxymKyK4kQKZdzoCoe0L5RqpiV2eK0t4zx-d3JPHlISZ0P1nQdSZsxuA5SRlDB0MZWLw";
    const applicationServerKey = urlB64ToUint8Array(applicationServerPublicKey);
    swRegistration.pushManager.subscribe({
        userVisibleOnly: true,
        applicationServerKey: applicationServerKey
    })
    // 用户同意
    .then(function(subscription) {
        console.log('User is subscribed:', JSON.stringify(subscription));
        jQuery.post("/add-subscription.php", {subscription: JSON.stringify(subscription)}, function(result) {
            console.log(result);
        });
    })
    // 用户不同意或者生成失败
    .catch(function(err) {
        console.log('Failed to subscribe the user: ', err);
    });
}

subscribe传两个参数,一个是userVisibleOnly,这个表示消息是否必须要可见,如果设置为不可见,Chrome将会报错:

代码语言:javascript
复制
Chrome currently only supports the Push API for subscriptions that will result in user-visible messages. You can indicate this by calling pushManager.subscribe({userVisibleOnly: true}) instead. See https://goo.gl/yqv4Q4 for more details.

但其实这个并不影响,我们设置成true,但是收到消息后可以不用弹框,可以调postMessage去通知页面做相应的操作。

第二个参数applicationServerKey是服务端的公钥,这个可以用web push的Node包生成,先安装一个: npm install web-push --save

然后用以下代码生成:

代码语言:javascript
复制
const webpush = require('web-push');
//VAPID keys should only be generated only once.
const vapidKeys = webpush.generateVAPIDKeys();
console.log(vapidKeys.publicKey, vapidKeys.privateKey);

每运行一次就会生成一对新的密钥对,如:

代码语言:javascript
复制
publicKey:  BMgkd1qfOfI6vFBbxIFMgdxDGC6-j8NYTwF_MXOIZ-St9lPhhMdPuUyFfwg1DLY59WP0FEaX84ZJRwgztdpfBHs
privateKey: LUeSF8DCv-NBxIfaeWeKTux858H45_V75vT0zZQLEbY

公密钥只要能配套就好,公钥在浏览器端使用,用来生成subscription,密钥在服务端使用,用来发Push。

如果用户同意浏览器就会向FCM服务请求生成subscription,然后执行Promise链里的then,返回该subscription,在这个then里面把这个subscription发给服务端存起来。 反之,如果用户不同意,或者用户无法连到FCM的服务器将会抛异常:DOMException: Registration failed - push service error

生成的subscription大概长这样:

代码语言:javascript
复制
{"endpoint":"https://fcm.googleapis.com/fcm/send/ci3-kIulf9A:APA91bEaQfDU8zuLSKpjzLfQ8121pNf3Rq7pjomSu4Vg-nMwLGfJSvkOUsJNCyYCOTZgmHDTu9I1xvI-dMVLZm1EgmEH0vDA7QFLjPKShG86W2zwX0IbtBPHEDLO0WgQ8OIhZ6yTnu-S","expirationTime":null,"keys":{"p256dh":"BAdAo6ldzRT5oCN8stqYRemoihPGOEJjrUDL6y8zhdA_swao_q-HlY_69mzIVobWX2MH02TzmtRWj_VeWUFMnXQ=","auth":"SS1PBnGwfMXjpJEfnoUIeQ=="}}
(2)说了这么久的FCM,FCM到底是什么呢?

FCM官方是这么介绍的:

Firebase 云信息传递 (FCM) 是一种跨平台消息传递解决方案,可供您免费、可靠地传递消息。

使用 FCM,您可以通知客户端应用存在可同步的新电子邮件或其他数据。您可以发送通知消息以再次吸引用户并促进用户留存。在即时消息传递等使用情形中,一条消息可将最大 4KB 的有效负载传送至客户端应用。

FCM是一种可靠的消息传递平台,它最大的优点是同一套Push机制可以在IOS/Android/Web三端使用:

这个意义是很大的,因为Android的推送一直都比较乱,国内有些APP使用小米的Push服务,有些使用百度的,还有些使用腾讯的信鸽等等,这些Push都需要在后台运行线程,并且不能休眠,这就导致了手机在休眠状态时仍然有很多线程在运行着,使得手机耗电速度很快。最后还直接导致今年工信部出台要成立安卓统一推送联盟。 而苹果有一套统一的推送机制,大家把Push发给苹果的服务器,然后再由苹果下发给相应的苹果设备。Safari现在不支持Service Worker,但是可以用Apple Push,缺点是这种推送苹果说不能用来发送重要的数据,并且目测只能弹框显示,没办法在后台处理消息而不弹框。

(3)发送推送

发送推送可以用FCM提供的web push的库,它支持多种语言,包括Node.js/PHP等版本。 用Node.js可以这样发Push:

代码语言:javascript
复制
const webpush = require('web-push');
// 从数据库取出用户的subsciption
const pushSubscription = {"endpoint":"https://fcm.googleapis.com/fcm/send/ci3-kIulf9A:APA91bEaQfDU8zuLSKpjzLfQ8121pNf3Rq7pjomSu4Vg-nMwLGfJSvkOUsJNCyYCOTZgmHDTu9I1xvI-dMVLZm1EgmEH0vDA7QFLjPKShG86W2zwX0IbtBPHEDLO0WgQ8OIhZ6yTnu-S","expirationTime":null,"keys":{"p256dh":"BAdAo6ldzRT5oCN8stqYRemoihPGOEJjrUDL6y8zhdA_swao_q-HlY_69mzIVobWX2MH02TzmtRWj_VeWUFMnXQ=","auth":"SS1PBnGwfMXjpJEfnoUIeQ=="}};

// push的数据
const payload = {
    title: '一篇新的文章',
    body: '点开看看吧',
    icon: '/html/app-manifest/logo_512.png',
    data: {url: "https://www.rrfed.com"}
  //badge: '/html/app-manifest/logo_512.png'
};

webpush.sendNotification(pushSubscription, JSON.stringify(payload));

经实验,在大多数情况下这个延迟基本在1s以内,这边刚按下回车运行完,那边浏览器就收到了,但是有时候会发送失败(国内网络问题?)。 如果这个代码要在服务端运行的话,那么你应该需要一台中国香港的服务器。假如把发Push的数据和服务放在中国香港的服务器,需要发Push的时候由华北的服务器做个中转向这台服务器发请求。 只要用户能连上FCM那就可以愉快地发Push了,如果用户连不上那就没办法。

(4)接收推送消息

用运行在后台的Service Worker接收,监听push事件:

代码语言:javascript
复制
this.addEventListener('push', function(event) {
    console.log('[Service Worker] Push Received.');
    console.log(`[Service Worker] Push had this data: "${event.data.text()}"`);

    let notificationData = event.data.json();
    const title = notificationData.title;
    // 可以发个消息通知页面
    //util.postMessage(notificationData); 
    // 弹消息框
    event.waitUntil(self.registration.showNotification(title, notificationData));
});

主要是调用showNotification进行弹框,或者是使用postMessage通知页面相应地做些处理。 经实验,如果用户关闭了浏览器,在关闭期间如果有Push的话等到用户重新打开浏览器会再弹出来。 然后用户可以点击弹出来的框打开一个指定的页面,这个需要监听notificationclick事件:

代码语言:javascript
复制
this.addEventListener('notificationclick', function(event) {
    console.log('[Service Worker] Notification click Received.');

    let notification = event.notification;
    notification.close();
    event.waitUntil(
        clients.openWindow(notification.data.url)
    );
});

调用clients.openWindow打开一个新的页面,这样就基本完成了一个push推送的搭建。

Service Worker让我们在Web端也能有像原生APP一样的Push通知,使得Web端越来越像原生APP端,随着HTML5的其它新功能如WebAssembly提高运行速度,WebWorker多线程支持,数据库支持大量数据的管理和支持,Websocket进行实时通信,WebRTC进行P2P多媒体传输,还有WebGL、新进的WebVR等,使得在浏览器端能够做的事情越来越多,体验越来越丰富,而且这种Web APP还是跨平台的。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019-07-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • h5的Notification
    • 语法
      • 参数
      • web push
        • web push 优点
          • web push 国内使用少的原因
            • web push 流程
              • web push实现细节
                • 解释:
                  • web-push请参见github: https://github.com/web-push-libs/web-push
                    • 兼容性及其他问题
                    • 这就是整一个过程,具体来说:
                      • (1)浏览器发起询问,生成subscription
                        • (2)说了这么久的FCM,FCM到底是什么呢?
                          • (3)发送推送
                            • (4)接收推送消息
                            领券
                            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档