Skip to content
+-------+           +--------------+       +-------------+
|  UA   |           | Push Service |       | Application |
+-------+           +--------------+       |   Server    |
    |                      |               +-------------+
    |      Subscribe       |                      |
    |--------------------->|                      |
    |       Monitor        |                      |
    |<====================>|                      |
    |                      |                      |
    |          Distribute Push Resource           |
    |-------------------------------------------->|
    |                      |                      |
    :                      :                      :
    |                      |    Push Message(1)   |
    |   Push Message(2)    |<---------------------|
    |<---------------------|                      |
    |                      |                      |

浏览器提供 push service,浏览器可以跟 push service 订阅事件,订阅后 push server 会 监控浏览器。 后台服务器想与浏览器通信的话,许岚气需要把订阅事件后返回的对象交给服务器,服务器拿到后就可以 使用这个对象去推送消息,推送的消息会转交给 push service,push service 再给浏览器 推送消息

支持断网推送,当断网时,推送的消息会缓存在 push service 中,用户连网后,会再推送给浏览器

js
// 为了性能,在资源加载完毕后注册。
window.addEventListener('load', async () => {
  if (navigator.serviceWorker) {
    try {
      // 每次 sw.js 变化都会重新注册一个 service worker
      const registration = await navigator.serviceWorker.register('/sw.js')
      // service worker 激活成功后
      await navigator.serviceWorker.ready

      // userVisibleOnly: 布尔值,表示返回的推送订阅将只能被用于对用户可见的消息。
      // applicationServerKey:推送服务器用来向客户端应用发送消息的公钥。
      // 该值是应用程序服务器生成的签名密钥对的一部分,可使用在 P-256 曲线上实现的
      // 椭圆曲线数字签名(ECDSA)。可以是DOMString 或 ArrayBuffer。
      const pushSubscription = await registration.pushManager.subscribe({
        userVisibleOnly: true,
        applicationServerKey: urlBase64ToUint8Array('BMTiHmeUpn-Gw7CWKe4qmR3oI7xdCOZ9DL3XWV1QyV_7TlWBqOKIuIQaSy7_3bQo-O9iKkpG0tpBezKpTXUUmIE')
      })

      // pushSubscription
      // {
      //   endpoint: 'https://wns2-sg2p.notify.windows.com/w/?token=BQYAAACdH3o%2bxAZ6lnzQYNYrrew3qKNpATuZQ2zeF29cg3scuIeFHWh5zGvvaVYE7ate1leM8t9tIZmw7viPtixBN9%2fRp3kWElmwhBfJem4TdMOF%2fqBiZwdwiuAV0vSCb3O7agYfH4Jf7ZHWbNNtfz%2fHGhxDBHqtIBDQ%2bWQx0EGSUUAWanEYjeR55wFaiskI%2fhQqZAqdsrmC709HYZwD3p30VzXJ2STZJrAYFDEZJvCgxt0smK%2fZ%2f4ewML3WsonfCCuytRNDrFCJAbqPUCiib%2b4Dcoweo9KqzdEGJZVM6pThm76W57kQfAKfI%2fIVeXvAsj3qu0lkoKYn5RthSzDTtpp4t2Uo',
      //   expirationTime: null,
      //   keys: {
      //     p256dh: 'BFa48aljCmeXzok3MsUeM_BOoW7qfCZhqzHav4fR2hZ5qWyrPAwb0025HI7iRkJAGOrFilCS3idaICZh-qe_osw',
      //     auth: '3O4jf4avBAQsP4TVIZ-WAw'   
      //   }
      // }

      // 转交给服务器,服务器用它来通知 pushService,从而通知客户端。
      fetch('/add-sub', {
        headers: {
          'Content-Type': 'application/json'
        },
        method: 'post',
        body: JSON.stringify(pushSubscription)
      })
    } catch (error) {
      console.log(error)
    }
  }
})
js
// sw.js
// 监听 push service 的推送消息
self.addEventListener('push', e => {
  // 通过提示框展示
  self.registration.showNotification(e.data.text())
})

// 用户点击提示框时打开一个新页面
self.addEventListener('notificationclick', e => {
  clients.openWindow('http://www.baidu.com')
})
js
// server.js
const subs = []

app.post('/add-sub', (req, res) => {
  subs.push(req.body)
  res.end('')
})

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

// const vapidKeys = webpush.generateVAPIDKeys();
const vapidKeys = {
  publicKey: 'BMTiHmeUpn-Gw7CWKe4qmR3oI7xdCOZ9DL3XWV1QyV_7TlWBqOKIuIQaSy7_3bQo-O9iKkpG0tpBezKpTXUUmIE',        
  privateKey: 'DbOcDPBXhIApgr9gA8iu8uS4TPaVBN9B2EXWqBXLROg'
}

webpush.setVapidDetails(
  'mailto: 3385842328@qq.com',
  vapidKeys.publicKey,
  vapidKeys.privateKey
);

app.get('/push', (req, res) => {
  subs.forEach(sub => webpush.sendNotification(sub, 'msg'))
  res.end()
})