欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

ServiceWorker消息推送教程

程序员文章站 2022-07-04 11:35:16
> purpose的博客... > pwa资料整理(三):service worker 消息推送... --> pwa资料整理(三):serv...

    pwa资料整理(三):service worker 消息推送

    pwa资料整理(三):service worker 消息推送

    系列链接消息推送消息推送整体架构消息推送具体流程参考资料

    本篇是 pwa 资料整理的第三篇,主要介绍 service worker 所实现的消息推送相关内容。

    系列链接

    manifest 添加到桌面 service worker 离线缓存 service worker 消息推送(本篇)

    消息推送

    说到消息推送就想到 http2 push。实际上 http2 push 就是一个缓存相关的机制,与消息推送没有关联。
    拓展资料:http/2 push is tougher than i thought(我之前的博客中也有对该文章的理解,如果懒得看英文原文的话可以直接看我的内容概括)。

    事实上,同 sw 实现的消息推送一样,sw 本身仅仅是提供类似 web worker 的功能,需要结合 cache api 才能实现离线缓存功能。使用 sw 实现消息推送需结合 notification api,具体功能就不介绍,可以查看 mdn 文档。

    消息推送整体架构

    消息推送看起来似乎很简单,就是服务端给客户端发送消息这样简单粗暴的东西嘛,其实并不是。
    实际上,服务端无法直接给客户端发送消息,而必须借助 push service 进行消息的推送。webpush 的整体架构如下图所示:
    ServiceWorker消息推送教程
    这里的 ua 即 user agent,也就是用户代理;application server 为应用的服务端;push service 也就是推送服务,指的是 google 的 fcm (以前叫 gcm),或者 apple 的 apns(苹果现在还不支持 webpush)等。

    消息推送具体流程

    在使用消息推送之前,客户端需要通过 sw 进行消息订阅:

    同样是在注册 service worker 时,先通过 pushmanager.getsubscription方法获取当前客户端是否已经订阅过了,没有订阅则需要对推送订阅对象进行生成; 在向 push server 发送请求(pushmanager.subsribe)之前,已经生成了一个推送订阅对象; 得到响应之后,客户端会将推送服务生成的地址(end point)加入这个推送订阅对象中; 之后,客户端将推送订阅对象发送给服务端。

    一个完整的推送订阅对象数据结构如下:

    {
        "endpoint": "https://fcm.googleapis.com/fcm/send/...",
        "keys": {
            "p256dh" : "bncrd...",
            "auth"   : "tbhi..."
        }
    }
    

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

    sw 可以通过监听 push 事件来获取服务端所推送的消息。并通过 registration.shownotification方法显示消息。
    参考代码如下所示:

    // service-worker.js
    // ...
    const onpush = function(event) {
        event.waituntil(_self.registration.shownotification('new post arrival', {
            icon: '/logo.png'
        }));
    };
    
    _self.addeventlistener('push', onpush);
    

    服务端需要 push 消息时,只需要向用户订阅信息中的 url post 消息内容就行了,具体的消息分发由 push server 代为实现。服务端代码如下:

    // 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;
                });
        });
    

    在消息推送的过程中,必不可免的存在一些安全性相关的问题,不过这些都交由 push server 完成,开发者只需要关心客户端和服务端就可以了。需要注意的是:浏览器关闭时,sw 的线程会被回收,也就意味着消息无法接收。