likes
comments
collection
share

两句话带你入门webPush

作者站长头像
站长
· 阅读数 18

前言:

  经常做海外项目的一定听过或者知道webPush也就是消息推送,最近这一阵子在做PWA免不了有些需求在探索这方面,以下是调研的一些结果案例希望能帮助到大家。好了废话不多说我们直接开始!

基本概念

  Web Push是一种用于在网页浏览器中发送实时通知的技术。它允许网站向用户发送推送通知,而无需用户在网站上打开或保持浏览器标签打开。Web Push利用浏览器的推送服务,通过请求和订阅的方式将通知推送到用户设备上。

一般收到消息的状态是这样的,如下图:

两句话带你入门webPush

准备环境

  1. MacBook
  2. Node v16.19.0
  3. http-server 全局指令
  4. Web-push 消息通知
  5. FCM(Firebase Cloud Messaging)

Client消息推送

  Notification API 是一个浏览器提供的接口,用于向用户显示通知消息。通过使用该 API,开发者可以向用户的操作系统或浏览器发送推送式的通知,下面让我们简单创建一个实例了解一下:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>消息测试demo</title>
  </head>
  <body>
    <button>Click</button>
    <script>
      const button = document.querySelector("button");

      button.addEventListener("click", () => {
        Notification.requestPermission().then((perm) => {
          const notification = new Notification("我是测试");

          notification.addEventListener("error", (e) => {
            alert(" 请尝试用户打开权限设置 ");
          });
          if (perm === "granted") {
            const notification = new Notification("我是测试");

            notification.addEventListener("error", (e) => {
              alert(" Error: ");
            });
          }
        });
      });
    </script>
  </body>
</html>

执行全局的http-server的命令

http-server .
// 访问一下 http://localhost:8080/index.html

Tips: 详细代码可以点击 这里

Server && Client 配合使用

  简单的消息通知客户端可以控制,那么如何进行服务器分发和在浏览器未开启的时候进行推送呢?我们需要借助一个工具「web-push」下面让我们简单的来看一下!

express为例

初始化依赖

yarn add body-parser express web-push -S

利用web-push生成秘钥,消息的类型需要遵守web推送消息的协议

npx web-push generate-vapid-keys 
// or 执行任意一行都可以
npx web-push generate-vapid-keys --json

结果如下:

两句话带你入门webPush

有了初始的秘钥我们需要Node 启动一个服务器来接口客户端的请求,比如:

// server.js
const express = require('express');
const webpush = require('web-push');
const bodyParser = require('body-parser');
const path = require('path');

const app = express();

app.use(bodyParser.json());

app.use(express.static(path.join(__dirname, "client")))

// 实际秘钥要进行加密存储,同时进行接口下发到客户端
const publicVapidKey = "xxxxx";
const privateVapidKey = "xxxxx";  // 跟客户端publicVapidKey保持一致。

webpush.setVapidDetails("mailto:test@test.com", publicVapidKey, privateVapidKey);

app.post('/subscribe', (req, res) => {
    const subscription = req.body;
    res.status(201).json({});
    const payload = JSON.stringify({ title: "测试消息", body: "hello啊 老铁" });

    webpush.sendNotification(subscription, payload).catch(console.log);
})

const PORT = 5001;

app.listen(PORT, () => {
    console.log(`http://localhost:${PORT}/`);
});

客户端请求

// clent.js

const publicVapidKey = "xxxx"; // 跟server的privateVapidKey 保持一致

if ("serviceWorker" in navigator) {
  registerServiceWorker().catch(console.log);
}

async function registerServiceWorker() {
  const register = await navigator.serviceWorker.register("./worker.js", {
    scope: "/",
  });

//  详细: https://github.com/web-push-libs/web-push#using-vapid-key-for-applicationserverkey
  const subscription = await register.pushManager.subscribe({
    userVisibleOnly: true,
    applicationServerKey: publicVapidKey,
  });

  await fetch("/subscribe", {
    method: "POST",
    body: JSON.stringify(subscription),
    headers: {
      "Content-Type": "application/json",
    },
  });
}

通过Service Worker来监听服务器推送的消息

// worker.js

self.addEventListener('push', function(e) {
    const data = e.data.json();
    self.registration.showNotification(
        data.title,
        {
            body: data.body,
        }
    );
})

效果如下:

两句话带你入门webPush

Tips: 详细代码可以点击 这里

FCM(Firebase Cloud Messaging)

  因为做海外的关系我们通常都使用google的生态比如说是Firebase这里面集成了很多的轮子,有感兴趣的小伙伴可以研究一下这里就不在赘述了,本次的重点就是FCM(Firebase Cloud Messaging)

Firebase Cloud Messaging (FCM) :是一种跨平台消息传递解决方案,可供您可靠地传递消息,且无需任何费用。

交互界面如下:

两句话带你入门webPush

投放的状态如下、等待投放完成即可

两句话带你入门webPush

如果第一次的话 可能需要创建一下

两句话带你入门webPush

其实我们了解了node服务器发送的消息后,原理也是大同小异详细可以参考文档

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <div class="container">
      <div>hello 这是测试消息</div></div>
      <div class="message" style="min-height: 80px"></div>
      <div>发送消息的token:</div>
    </div>

    <script src="https://www.gstatic.com/firebasejs/9.14.0/firebase-app-compat.js"></script>
    <script src="https://www.gstatic.com/firebasejs/9.14.0/firebase-messaging-compat.js"></script>

    <script>
      const firebaseConfig = {
        apiKey: "xxxxx",
        authDomain: "xxxxx",
        projectId: "xxxxx",
        storageBucket: "xxxxx",
        messagingSenderId: "xxxxx",
        appId: "xxxxx",
        measurementId: "xxxxx"
      };

      const app = firebase.initializeApp(firebaseConfig);
      const messaging = firebase.messaging();
      messaging
        .getToken({
          vapidKey: `xxxxx`,
        })
        .then((currentToken) => {
          if (currentToken) {
            document.querySelector("body").append(currentToken);
            sendTokenToServer(currentToken);
          } else {
            setTokenSentToServer(false);
          }
        })
        .catch((err) => {
          console.log(err);
          // if error
          setTokenSentToServer(false);
        });

      messaging.onMessage((payload) => {
        console.log("Message received ", payload);
        const messagesElement = document.querySelector(".message");
        const dataHeaderElement = document.createElement("h5");
        const dataElement = document.createElement("pre");
        dataElement.style = "overflow-x: hidden;";
        dataHeaderElement.textContent = "Message Received:";
        dataElement.textContent = JSON.stringify(payload, null, 2);
        messagesElement.appendChild(dataHeaderElement);
        messagesElement.appendChild(dataElement);

        Notification.requestPermission().then((permission) => {
          if (permission === "granted") {
            const notification = new Notification(payload.notification.title, {
              body: payload.notification.body,
            });
            notification.onclick = (event) => {
              event.preventDefault(); 
              window.open(payload.data.link, "_blank");
            };
          }
        });
      });

      function sendTokenToServer(currentToken) {
        if (!isTokenSentToServer()) {
          setTokenSentToServer(true);
        } else {
          console.log("Token already available in the server");
        }
      }
      function isTokenSentToServer() {
        return window.localStorage.getItem("sentToServer") === "1";
      }
      function setTokenSentToServer(sent) {
        window.localStorage.setItem("sentToServer", sent ? "1" : "0");
      }
    </script>
  </body>
</html>

Service Worker 部分

// firebase-messaging-sw.js

importScripts('https://www.gstatic.com/firebasejs/9.14.0/firebase-app-compat.js')
importScripts('https://www.gstatic.com/firebasejs/9.14.0/firebase-messaging-compat.js')

const firebaseConfig = {
    apiKey: "xxxxx",
    authDomain: "xxxxx",
    projectId: "xxxxx",
    storageBucket: "xxxxx",
    messagingSenderId: "xxxxx",
    appId: "xxxxx",
    measurementId: "xxxxx",
  };
const app = firebase.initializeApp(firebaseConfig)
const messaging = firebase.messaging()

实现的效果如下:

Tips: 详细代码可以点击 这里

收不到消息?

以下是我在本地测试的时候遇到的问题,如果大家遇到其他的问题也欢迎补充~

Macbook没开启消息通知

Mac 电脑开发中需要在系统中开启消息通知:【系统偏好设置】=> 【通知】=> 【chrome 开启通知】

两句话带你入门webPush两句话带你入门webPush两句话带你入门webPush

未开启chrome的实验性功能

  • 在Chrome浏览器中访问地址:chrome://flags
  • 搜索栏中搜索:notifications,找到 Enable system notifications 选项,将其选项值改为 Disabled,重启浏览器,问题解决。

两句话带你入门webPush

最后

其实我在整理文章的时候同时也在思考一些问题,下面做了陈列

为什么国内做的少?

Web push 消息推送在海外相对较多的原因主要有以下几点:

  1. 平台支持:海外的浏览器和操作系统广泛支持 Web push 技术,包括 Chrome、Firefox、Safari 等主流浏览器,以及 Android 和 iOS 等主流操作系统。相比之下,国内浏览器和操作系统对于 Web push 的支持相对较少,限制了国内开发者开展相关业务。
  2. 开放程度:海外环境中的开发者更容易获取到 Web push 技术相关的开发文档、API 接口等资源,而国内的 Web push 相关技术多数由国内厂商自有的推送服务提供,对于外部开发者或者独立开发团队来说,参与门槛相对较高。
  3. 用户审核限制:根据国内法规和政策,App 需要完成国家相关部门的审核才能够向用户发送推送消息,这增加了国内 Web push 业务的运营成本和法规合规的要求,相比之下,海外环境中更灵活。
  4. 市场需求与文化差异:海外市场对于个性化推送、即时通知等需求较高,而且海外用户更接受优质推送服务。而国内用户普遍倾向于使用即时通信工具、社交媒体等方式获取信息和推送服务,对于 Web push 的需求相对较少。

综上所述,海外在 Web push 消息推送领域的发展较为成熟,而国内因为各种技术、法规和市场的限制,导致其规模相对较小。

当浏览器同时开启了HTTP缓存策略和Service Worker时,缓存加载的策略顺序如下:

  1. 首先,浏览器会检查Service Worker中的缓存策略。Service Worker是一个独立于网页的脚本,可以控制页面的网络请求和响应。如果Service Worker中的缓存中有匹配的资源,浏览器将直接从缓存获取资源,避免发起网络请求。
  2. 如果Service Worker中没有匹配的缓存资源,则浏览器会检查HTTP缓存。HTTP缓存是基于浏览器和服务器之间的通信协议,在HTTP响应头中使用Cache-Control、Expires等字段来定义资源的缓存行为。浏览器会根据这些字段来判断是否需要重新从服务器请求资源。
  3. 如果HTTP缓存也没有匹配的资源,浏览器会发起网络请求,从服务器获取最新的资源。

需要注意的是,Service Worker的缓存策略优先级高于HTTP缓存策略。因此,如果Service Worker中有匹配的缓存资源,即使HTTP缓存设置了不被缓存,浏览器也会优先使用Service Worker中的缓存。

参考资料:

结语

以上就是本次web push 的全部的内容,如果能帮助你的话记得点赞收藏哦~ 再见了 老baby们