https://developers.google.com/web/fundamentals/codelabs/push-notifications/?hl=en

首先下载源码:

git clone https://github.com/GoogleChrome/push-notifications.git

设置如下选项方便开发:

开始

注册之后记录sw实例:

  navigator.serviceWorker.register('sw.js')
.then(function(swReg) {
console.log('Service Worker is registered', swReg);
swRegistration = swReg;
})

生成key:

https://web-push-codelab.glitch.me/。生成了一个相互对应的public key 与 private key

然后把public key记录到 applicationServerPublicKey变量上。

判断当前sw是否已经订阅过消息推送了:

  swRegistration.pushManager.getSubscription()
.then(function(subscription) {
isSubscribed = !(subscription === null); if (isSubscribed) {
console.log('User IS subscribed.');
} else {
console.log('User is NOT subscribed.');
}
});

使用之前生成的public key来订阅消息推送:

const applicationServerKey = urlB64ToUint8Array(applicationServerPublicKey);

// subscribe 会给推送服务器发送一个网络请求
swRegistration.pushManager.subscribe({
userVisibleOnly: true, // 用于显示请求权限的界面,所以这个值基本必须为true,否则获取不到权限的话,当前promise会被reject
applicationServerKey: applicationServerKey }).then(function (subscription) {
// 订阅成功。subscription 就是推送服务器返回的信息
console.log('User is subscribed.');
updateSubscriptionOnServer(subscription); // 在这个自定义函数中,我们应该把订阅信息发送给后端
isSubscribed = true; }).catch(function (err) {
console.log('Failed to subscribe the user: ', err);
}); // 工具函数|
function urlB64ToUint8Array(base64String) {
const padding = '='.repeat((4 - base64String.length % 4) % 4);
const base64 = (base64String + padding)
.replace(/\-/g, '+')
.replace(/_/g, '/'); const rawData = window.atob(base64);
const outputArray = new Uint8Array(rawData.length); for (let i = 0; i < rawData.length; ++i) {
outputArray[i] = rawData.charCodeAt(i);
}
return outputArray;
}

执行订阅的时候,界面上会有如下弹框来请求消息推送的显示权限:

点击同意的话,则订阅成功。但如果用户点击了拒绝,则app没办法再次显示这个弹框而且没有消息推送,以下这个值会为true:

Notification.permission === 'denied'

手动点击这里(ask),可以撤销权限,使弹窗再次弹出来,方便开发测试:

处理消息推送

我们需要在sw中监听push事件,来接收服务器发来的消息推送:

self.addEventListener('push', function(event) {
console.log('[Service Worker] Push Received.');
console.log(`[Service Worker] Push had this data: "${event.data.text()}"`); const title = 'Push Codelab';
const options = {
body: 'Yay it works.',
icon: 'images/icon.png',
badge: 'images/badge.png' //仅仅用在安卓
}; // showNotification 用于显示一个通知
   // waitUntil :使sw等待直至这个promise被处理,否则有可能这个promise没被处理,sw 就被浏览器终止了
event.waitUntil(self.registration.showNotification(title, options));
});

测试:在这里点击push:

屏幕左下角就会看到这个通知:

但是点击这个通知是没什么响应的,需要我们去注册一个点击事件:

self.addEventListener('notificationclick', function(event) {
console.log('[Service Worker] Notification click Received.'); event.notification.close(); // 关闭这个通知 event.waitUntil(
clients.openWindow('https://developers.google.com/web/') // 打开一个标签
);
});

发送消息推送

  以上订阅成功后返回的subscription,将它 JSON.stringify(subscription) 后的字符串粘贴到 https://web-push-codelab.glitch.me/ 就可以发送用于测试的消息推送了(注意要用页面所在的key来订阅才可以)。

  同理在实际应用中,我们后端也需要这个subscription信息来发送消息推送。步骤如下(使用 web-push):

创建firebase项目,里面的key为(用来作为GCM API key):

然后在https://web-push-codelab.glitch.me/ 中生成的public/private key为(其实也可以用webpush.generateVAPIDKeys来生成):

接着来订阅消息推送:

function urlB64ToUint8Array(base64String) {
const padding = '='.repeat((4 - base64String.length % 4) % 4);
const base64 = (base64String + padding)
.replace(/\-/g, '+')
.replace(/_/g, '/'); const rawData = window.atob(base64);
const outputArray = new Uint8Array(rawData.length); for (let i = 0; i < rawData.length; ++i) {
outputArray[i] = rawData.charCodeAt(i);
}
return outputArray;
} const applicationServerPublicKey = 'BP0bPsBFRO4JI4WPI-0Hztl49AX2mjfPxr5SAmiu9i1C4T1X2EFQvuoCekow-JD9Gs3aHlkxstVm9UTndHA0YM8'; function subscribeUser() {
const applicationServerKey = urlB64ToUint8Array(applicationServerPublicKey);
swRegistration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: applicationServerKey
})
.then(function(subscription) {
console.log('User is subscribed.'); updateSubscriptionOnServer(subscription); isSubscribed = true; updateBtn();
})
.catch(function(err) {
console.log('Failed to subscribe the user: ', err);
updateBtn();
});
}

node服务器来发送消息推送:

const webpush = require('web-push');

// VAPID keys should only be generated only once.
// const vapidKeys = webpush.generateVAPIDKeys(); webpush.setGCMAPIKey('AIzaSyAPNqXa931TMPdEx5im92uDQmWQKfKFJNo');
webpush.setVapidDetails(
'mailto:947133297@qq.com',
"BP0bPsBFRO4JI4WPI-0Hztl49AX2mjfPxr5SAmiu9i1C4T1X2EFQvuoCekow-JD9Gs3aHlkxstVm9UTndHA0YM8",
"Y23-foXK_oHtxOA5whmR61RBbyqqm9Sxnl-bapZPghQ"
); // This is the same output of calling JSON.stringify on a PushSubscription
const pushSubscription = {
endpoint: 'https://fcm.googleapis.com/fcm/send/fN0CygRBHVo:APA91bH4FB9bkE6RjD6v758TaNoHIx4IhUxdSm_bcFMPRRnyY4IcTlID9md6AwAdhUhqE7HzbL76WY6Wzak7MGmtrJ5InYAwYP31B-mc-TXRCnKQwUKxjIPe1Kv6-U_S672rG_8jVmpJ',
keys: {
auth: 'uOxqcnlXYQIyDucqXeWeeA==',
p256dh: 'BFoO1hMB5kpWA4lPx2fKZGiyw3Qd-3n9afeE3jrJ62Bna66LsHQmCSIjo0Q9t2UF6MZdzyqe6cNkNbSGpNpmX6I='
}
}; webpush.sendNotification(pushSubscription, 'Your Push Payload Text').then(()=>{
console.log("发送完成")
}).catch((err)=>{
console.log("被拒绝")
console.log(err)
})

因为在中国被墙的原因,以上代码运行会报错:

被拒绝
{ Error: connect ETIMEDOUT 172.217.160.106:443
at Object._errnoException (util.js:1024:11)
at _exceptionWithHostPort (util.js:1046:20)
at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1182:14)
code: 'ETIMEDOUT',
errno: 'ETIMEDOUT',
syscall: 'connect',
address: '172.217.160.106',
port: 443 }

查看issue之后,发现有人针对这个问题提交了一个PR,但是没有被应用,即master分支上还是存在这个问题。

取消订阅

swRegistration.pushManager.getSubscription()
.then(function(subscription) {
if (subscription) {
// TODO: Tell application server to delete subscription
return subscription.unsubscribe();
}
})
.catch(function(error) {
console.log('Error unsubscribing', error);
})

并且要记得通知后端,不要往这个subscription推送消息了 。

service worker 消息推送的更多相关文章

  1. IOS - 消息推送原理和实现

    一.消息推送原理: 在实现消息推送之前先提及几个于推送相关概念,如下图1-1: 1.Provider:就是为指定IOS设备应用程序提供Push的服务器,(如果IOS设备的应用程序是客户端的话,那么Pr ...

  2. iOS 消息推送原理及实现Demo

    一.消息推送原理: 在实现消息推送之前先提及几个于推送相关概念,如下图1-1: 1.Provider:就是为指定IOS设备应用程序提供Push的服务器,(如果IOS设备的应用程序是客户端的话,那么Pr ...

  3. iOS 消息推送原理

    一.消息推送原理: 在实现消息推送之前先提及几个于推送相关概念,如下图: 1. Provider:就是为指定IOS设备应用程序提供Push的服务器,(如果IOS设备的应用程序是客户端的话,那么Prov ...

  4. iOS 消息推送原理及实现总结

    在实现消息推送之前先提及几个于推送相关概念,如下图:1. Provider:就是为指定IOS设备应用程序提供Push的服务器,(如果IOS设备的应用程序是客户端的话,那么Provider可以理解为服务 ...

  5. iOS 消息推送原理及实现总结 分类: ios技术 2015-03-01 09:22 70人阅读 评论(0) 收藏

    在实现消息推送之前先提及几个于推送相关概念,如下图: 1. Provider:就是为指定IOS设备应用程序提供Push的服务器,(如果IOS设备的应用程序是客户端的话,那么Provider可以理解为服 ...

  6. iOS开发消息推送原理

    转载自:http://www.cnblogs.com/cdts_change/p/3240893.html 一.消息推送原理: 在实现消息推送之前先提及几个于推送相关概念,如下图1-1: 1.Prov ...

  7. iOS消息推送原理

    推送相关概念,如下图1-1: 1.Provider:就是为指定IOS设备应用程序提供Push的服务器,(如果IOS设备的应用程序是客户端的话,那么Provider可以理解为服务端[消息的发起者]): ...

  8. APNS消息推送实现

    转自:http://blog.csdn.net/biaobiaoqi/article/details/8058503 一.消息推送原理: 在实现消息推送之前先提及几个于推送相关概念,如下图1-1: 1 ...

  9. iOS消息推送原理和实现总结

    一.消息推送原理: 在实现消息推送之前先提及几个于推送相关概念,如下图:1. Provider:就是为指定IOS设备应用程序提供Push的服务器,(如果IOS设备的应用程序是客户端的话,那么Provi ...

随机推荐

  1. Mybatis思

    站在巨人的肩膀上,感谢! mybatis源码分析之Mapper代理实现分析 2017年11月21日 23:39:04 huangshanchun 阅读数:277    版权声明:欢迎转载,如有不足之处 ...

  2. Yac - PHP扩展

    1:首先你要安装Git [root@localhost]# git clone https://github.com/laruence/yac 2:进入yac目录进行配置 [root@localhos ...

  3. python之内置函数(lambda,sorted,filter,map),递归,二分法

    一.lambda匿名函数 为了解决一些简单需求而设计的一句话函数,lambda表示的是匿名函数,不需要用def来声明,一句话就可以声明出一个函数. 语法: 函数名 = lambda 参数 : 返回值 ...

  4. Jenkins之发布报错“error: RPC failed; curl 18 transfer closed with outstanding read data remaining”

    报错信息: error: RPC failed; curl transfer closed with outstanding read data remaining fatal: The remote ...

  5. Crusher Django 学习笔记2 基本url配置

    http://crusher-milling.blogspot.com/2013/09/crusher-django-tutorial2-conf-basic-url.html 顺便学习一下FQ Cr ...

  6. 解决织梦 \include\userlogin.class.php on line 21(或16) 报错的方法

    用了下DEDECMS v5.7 SP1版本,发现很多问题,其中一个比较严重的是,架到服务器上的dede网站后台打开菜单选项卡得不能动,等半天显示505服务器错误,这个真让人纠结,在本地调试明明好好的, ...

  7. React 实践记录 03 React router

    Introduction 本文主要参考了react router 的官方文档. React Router是一套完整的配合React的路由解决方案,可能你已经知道前端路由,或者知道后端有路由的概念,如下 ...

  8. react-native入门学习( 一 )

    开发环境配置 因为个人电脑是windows7环境,所以在选择安装react-native 环境的时候是用的 windows+androidreact-native中文网文档地址  https://re ...

  9. 重新安装Magento CE 2.1.0

    删除 var/cache 文件夹 删除 var/generation文件夹 删除 app/etc/config.php 删除 app/etc/env.php 如果您觉得阅读本文对您有帮助,欢迎转载本文 ...

  10. System.TypeInitializationException: 'The type initializer for 'MySql.Data.MySqlClient.Replication.ReplicationManager' threw an exception.'

    下午在调试的时候报错数据库连接就报错我就很纳闷后面用原来的代码写发现还是报错 System.TypeInitializationException: 'The type initializer for ...