HOOOS

Service Worker 消息推送全攻略:原理、订阅、处理及可靠性保障

0 69 技术宅小push Service Worker消息推送Web Push
Apple

Service Worker 最令人兴奋的功能之一就是消息推送,它允许你的 Web 应用在后台接收来自服务器的消息,即使用户没有 actively 打开你的网站!想象一下,你的电商网站可以在用户购物车里的商品降价时,第一时间通知用户,或者你的新闻网站可以在有突发新闻时立即推送给用户,这该有多棒!

这篇文章将带你深入了解 Service Worker 消息推送的方方面面,即使你只有一点后端开发经验,也能轻松掌握。

1. 消息推送的流程:一次完整的旅程

要理解 Service Worker 的消息推送,咱们先来看一次完整的消息推送流程,就像一次旅行,从出发到抵达,每一步都清清楚楚:

  1. 用户订阅推送服务: 用户在你的网站上点击“允许通知”按钮,就像买了一张旅行票。浏览器会向推送服务(例如 Firebase Cloud Messaging (FCM) 或 Apple Push Notification service (APNs))请求一个唯一的“订阅凭证”(Subscription),这个凭证包含了推送消息所需的所有信息,就像你的旅行票上的航班号、座位号等。
  2. 浏览器将订阅凭证发送到你的服务器: 浏览器拿到订阅凭证后,会通过你的 Web 应用将它发送到你的服务器,就像把旅行票交给旅行社。你的服务器需要安全地存储这个凭证,以便后续向该用户推送消息。
  3. 服务器发送推送消息: 当你的服务器需要向用户推送消息时,它会使用之前存储的订阅凭证,向推送服务发送一个推送请求,就像旅行社向航空公司发出起飞指令。推送请求中包含了要推送的消息内容,以及一些可选的参数,例如消息标题、图标等。
  4. 推送服务将消息发送到用户的浏览器: 推送服务收到推送请求后,会根据订阅凭证中的信息,找到用户的浏览器,并将消息发送过去,就像航空公司把飞机飞到目的地。
  5. 浏览器触发 Service Worker 的 push 事件: 用户的浏览器收到消息后,会唤醒负责处理推送消息的 Service Worker(即使你的网站没有打开!),并触发它的 push 事件,就像飞机降落后,机场工作人员开始处理后续事宜。
  6. Service Worker 处理推送消息: Service Worker 在 push 事件的处理函数中,可以获取到推送消息的内容,并根据你的业务逻辑进行处理,例如显示一条通知、更新应用数据等,就像机场工作人员引导旅客出站、提取行李等。
  7. 用户与通知交互: 用户看到通知后,可以点击通知,或者执行其他操作,这些操作会触发 Service Worker 的 notificationclick 事件,你可以在这个事件的处理函数中处理用户的交互,例如打开网站的某个页面、发送统计数据等,就像旅客离开机场,开始他们的旅程。

是不是很清晰?整个流程就像一次完整的旅行,每一步都有明确的角色和职责。

2. 订阅消息推送:拿到“旅行票”

要实现消息推送,首先要让用户订阅推送服务。这通常通过以下几个步骤完成:

  1. 检查浏览器是否支持推送服务: 不是所有浏览器都支持推送服务,所以我们需要先检查一下:

    if ('serviceWorker' in navigator && 'PushManager' in window) {
      // 浏览器支持 Service Worker 和 Push API
    } else {
      // 浏览器不支持
      console.warn('Push messaging is not supported.');
    }
    
  2. 注册 Service Worker: 如果浏览器支持,我们需要先注册一个 Service Worker:

    navigator.serviceWorker.register('/sw.js') // 假设你的 Service Worker 文件名为 sw.js
      .then(function(registration) {
        // 注册成功
        console.log('Service Worker registered with scope:', registration.scope);
      })
      .catch(function(error) {
        // 注册失败
        console.error('Service Worker registration failed:', error);
      });
    
  3. 请求推送权限: 注册 Service Worker 后,我们需要向用户请求推送权限:

     Notification.requestPermission(function(permission) {
        if (permission === 'granted') {
          // 用户允许推送
        } else if (permission === 'denied') {
          // 用户拒绝推送
        } else if (permission === 'default'){
          // 用户忽略了
        }
      });
    
  4. 订阅推送服务: 如果用户允许推送,我们可以使用 PushManager 接口来订阅推送服务:

    navigator.serviceWorker.ready.then(function(registration) {
      return registration.pushManager.subscribe({
        userVisibleOnly: true, // 必须设置为 true,表示推送的消息必须对用户可见
        applicationServerKey: urlBase64ToUint8Array(YOUR_VAPID_PUBLIC_KEY) // 你的 VAPID 公钥,用于标识你的应用
      });
    }).then(function(subscription) {
      // 订阅成功,将 subscription 发送到你的服务器
      console.log('Push subscription:', subscription);
      sendSubscriptionToServer(subscription);
    }).catch(function(error) {
      // 订阅失败
      console.error('Push subscription failed:', error);
    });
    

    注意:

    • userVisibleOnly 必须设置为 true,这是为了防止滥用推送服务,确保推送的消息必须对用户可见。
    • applicationServerKey 是一个 VAPID (Voluntary Application Server Identification) 公钥,用于标识你的应用。你需要生成一对 VAPID 公钥和私钥,公钥用于订阅推送服务,私钥用于签名推送请求。你可以使用一些在线工具或库来生成 VAPID 密钥对。
    • urlBase64ToUint8Array 函数用于将 base64 编码的 VAPID 公钥转换为 Uint8Array 类型,这是 applicationServerKey 参数要求的格式。这个函数的实现可以参考 MDN 文档
  5. 将订阅凭证发送到服务器: 订阅成功后,你会得到一个 PushSubscription 对象,它包含了推送消息所需的所有信息。你需要将这个对象发送到你的服务器,并安全地存储起来。通常使用fetch发送。

      function sendSubscriptionToServer(subscription) {
           fetch('/api/save-subscription', {
            method: 'POST',
            headers: {
              'Content-Type': 'application/json'
             },
             body: JSON.stringify(subscription)
      })
      .then(response => {
         if (!response.ok) {
            throw new Error('Bad status code from server.');
          }
         return response.json();
      })
        .then(responseData => {
        if (!(responseData.data && responseData.data.success)) {
          throw new Error('Bad response from server.');
          }
        });
    }
    

3. 处理推送消息:Service Worker 的舞台

当你的服务器向用户发送推送消息时,用户的浏览器会唤醒 Service Worker,并触发它的 push 事件。你需要在 Service Worker 中监听这个事件,并处理推送消息:

// sw.js
self.addEventListener('push', function(event) {
  console.log('Received a push message:', event);

  let title = '新消息';
  let options = {
    body: '你有一条新消息!',
    icon: '/images/icon.png',
    badge: '/images/badge.png',
    data: { // 自定义数据
      url: '/'
    }
  };

  if (event.data) {
    // 如果推送消息中包含了数据,可以使用这些数据
    const data = event.data.json();
    title = data.title || title;
    options.body = data.body || options.body;
  }

  event.waitUntil(
    self.registration.showNotification(title, options)
  );
});

代码解释:

  • self.addEventListener('push', ...):监听 push 事件。
  • event.data:获取推送消息中的数据。你可以使用 event.data.text()event.data.json()event.data.blob()event.data.arrayBuffer() 等方法来获取不同格式的数据。
  • self.registration.showNotification(title, options):显示一条通知。title 是通知的标题,options 是一个配置对象,可以设置通知的各种属性,例如正文、图标、自定义数据等。
  • event.waitUntil(...):非常重要!这个方法告诉浏览器,Service Worker 正在执行一个异步操作,不要过早地终止 Service Worker。你需要将所有异步操作(例如 showNotification)放在 event.waitUntil 中。
  • options.data是自定义数据,可以在推送时由服务器加入,用于客户端收到推送时使用。

4. 处理用户交互:notificationclick 事件

当用户点击通知时,Service Worker 会触发 notificationclick 事件。你可以在这个事件的处理函数中处理用户的交互:

// sw.js
self.addEventListener('notificationclick', function(event) {
  console.log('Notification click:', event.notification);

  event.notification.close(); // 关闭通知

  // 打开一个新的窗口/标签页
  event.waitUntil(
    clients.openWindow(event.notification.data.url)
  );
});

代码解释:

  • event.notification:获取被点击的通知对象。
  • event.notification.close():关闭通知。
  • clients.openWindow(url):打开一个新的窗口或标签页,并导航到指定的 URL。你可以根据 event.notification.data 中的自定义数据来决定打开哪个 URL。
  • event.waitUntil(...): 确保在Service Worker完成操作之前不会被终止。

5. 保证消息推送的可靠性:一些关键点

消息推送的可靠性非常重要,我们需要采取一些措施来确保消息能够成功送达,并且不会丢失或重复:

  • 使用 HTTPS: Service Worker 只能在 HTTPS 环境下工作,这是为了保证安全。

  • 处理推送失败: 推送服务可能会因为各种原因(例如网络问题、用户设备离线等)导致推送失败。你的服务器需要处理这些失败,并进行重试。通常,推送服务会提供一些机制来处理推送失败,例如指数退避重试、消息过期时间等。

  • 防止消息重复: 如果你的服务器在发送推送请求后没有收到推送服务的响应,它可能会认为推送失败,并再次发送相同的请求。这可能会导致用户收到重复的通知。为了防止这种情况,你可以在推送请求中添加一个唯一的标识符,并在 Service Worker 中检查这个标识符,如果已经处理过相同的标识符,就忽略这条消息。

  • 处理 Service Worker 更新: 当你的 Service Worker 代码更新时,浏览器会安装一个新的 Service Worker,但旧的 Service Worker 可能仍然在运行。这可能会导致一些问题,例如旧的 Service Worker 处理了新版本的推送消息。为了避免这种情况,你可以在新的 Service Worker 的 activate 事件中调用 clients.claim() 方法,立即接管所有客户端的控制权。

  • 用户体验: 不要滥用推送服务,频繁地向用户发送通知会打扰用户,导致用户关闭通知权限。你应该只在真正有价值的时候才向用户推送消息,并提供清晰的退订选项。

    • 只推送重要且紧急的通知
    • 允许用户自定义通知偏好
    • 提供明确的退订选项

总结:你已经掌握了 Service Worker 消息推送!

通过这篇文章,相信你已经对 Service Worker 消息推送有了深入的了解。从消息推送的流程,到订阅、处理、交互,再到可靠性保障,你已经掌握了所有关键知识点。现在,你可以开始在你的 Web 应用中实现消息推送功能了!

记住,Service Worker 消息推送是一个强大的工具,它可以让你的 Web 应用更加生动、更加有用。但同时,它也是一把双刃剑,你需要谨慎使用,不要滥用,才能真正发挥它的价值。

如果你想了解更多关于 Service Worker 的知识,可以参考以下资源:

祝你在 Service Worker 的世界里探索愉快!

点评评价

captcha
健康