PWA 进阶教程(二): 如何在 PWA 中实现推送通知

前言

在现代互联网应用中,推送通知是提升用户参与度和互动性的关键功能之一。对于进阶的网页应用(PWA),推送通知尤为重要,因为它可以让应用保持用户的关注,即使应用未被打开。本文将详细讲解如何在PWA中实现推送通知。

什么是 PWA

在开始之前,先简要介绍一下PWA。Progressive Web Apps(PWA)是一种能在网页和原生应用之间架起桥梁的技术,使得网页应用能够拥有类似原生应用的用户体验。PWA具有以下特点:

  • 响应式设计
  • 能够离线使用
  • 可安装到设备主屏幕
  • 提供推送通知

实现推送通知的步骤

实现推送通知主要分为以下几个步骤:

  1. 注册服务工作者
  2. 申请权限
  3. 订阅推送服务
  4. 处理推送事件

1. 注册服务工作者

服务工作者(Service Worker)是一个独立于网页的后台脚本,可以处理推送通知、离线缓存等功能。首先,我们需要注册一个服务工作者。

if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/service-worker.js')
    .then(function(registration) {
      console.log('Service Worker registered with scope:', registration.scope);
    }).catch(function(error) {
      console.log('Service Worker registration failed:', error);
    });
}

service-worker.js 文件是服务工作者的脚本文件,放置在服务器根目录下。

2. 申请权限

推送通知需要用户的权限许可,申请权限并处理用户的响应。

Notification.requestPermission().then(function(permission) {
  if (permission === 'granted') {
    console.log('Notification permission granted.');
  } else {
    console.log('Notification permission denied.');
  }
});

3. 订阅推送服务

一旦权限被授予,我们就可以订阅推送服务了。这里我们利用 Web Push API 和 VAPID(Voluntary Application Server Identification)来进行配置。

function subscribeUserToPush() {
  navigator.serviceWorker.ready.then(function(registration) {
    const applicationServerKey = urlBase64ToUint8Array('<Your Public VAPID Key>');
    registration.pushManager.subscribe({
      userVisibleOnly: true,
      applicationServerKey: applicationServerKey
    }).then(function(subscription) {
      console.log('User is subscribed:', subscription);
      // Send subscription to application server
    }).catch(function(error) {
      console.log('Failed to subscribe the user:', error);
    });
  });
}

function urlBase64ToUint8Array(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;
}

4. 处理推送事件

在服务工作者中,我们需要处理推送事件,并展示通知给用户。

self.addEventListener('push', function(event) {
  const data = event.data.json();
  const title = data.title;
  const options = {
    body: data.body,
    icon: data.icon,
    badge: data.badge
  };

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

发送推送通知需要一个后端服务来处理订阅数据并发送消息。通常我们会利用一些第三方服务如 Firebase Cloud Messaging(FCM)或者自行搭建的 Web Push 服务。

后端服务示例

下面是一个使用 Node.js 和 web-push 库发送推送通知的示例。

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

const vapidKeys = {
  publicKey: '<Your Public VAPID Key>',
  privateKey: '<Your Private VAPID Key>'
};

webPush.setVapidDetails(
  'mailto:<Your Email>',
  vapidKeys.publicKey,
  vapidKeys.privateKey
);

const subscription = { /* Subscription object received from client */ };

const payload = JSON.stringify({
  title: '推送通知标题',
  body: '推送通知内容',
  icon: '/path/to/icon.png',
  badge: '/path/to/badge.png'
});

webPush.sendNotification(subscription, payload).catch(error => {
  console.error('Error sending notification:', error);
});

订阅数据的管理

在实际应用中,订阅数据通常需要存储在后端数据库中,以便在需要时发送推送通知。下面是一个使用 Express 和 MongoDB 的示例,展示如何保存和管理订阅数据。

创建一个简单的 Express 服务器

首先,我们需要创建一个简单的 Express 服务器来处理订阅请求。

const express = require('express');
const bodyParser = require('body-parser');
const mongoose = require('mongoose');
const webPush = require('web-push');

const app = express();

app.use(bodyParser.json());

const vapidKeys = {
  publicKey: '<Your Public VAPID Key>',
  privateKey: '<Your Private VAPID Key>'
};

webPush.setVapidDetails(
  'mailto:<Your Email>',
  vapidKeys.publicKey,
  vapidKeys.privateKey
);

// MongoDB Model
const Subscription = mongoose.model('Subscription', new mongoose.Schema({
  endpoint: String,
  keys: {
    p256dh: String,
    auth: String
  }
}));

// Route to save subscription
app.post('/subscribe', (req, res) => {
  const subscription = new Subscription(req.body);
  subscription.save((err) => {
    if (err) {
      return res.status(500).send({ error: 'Failed to save subscription' });
    }
    res.status(201).send({ message: 'Subscription saved successfully' });
  });
});

// Route to send push notification
app.post('/sendNotification', (req, res) => {
  const payload = JSON.stringify(req.body);

  Subscription.find({}, (err, subscriptions) => {
    if (err) {
      return res.status(500).send({ error: 'Failed to retrieve subscriptions' });
    }

    subscriptions.forEach(subscription => {
      webPush.sendNotification(subscription, payload).catch(error => {
        console.error('Error sending notification:', error);
      });
    });

    res.status(200).send({ message: 'Notifications sent successfully' });
  });
});

mongoose.connect('<Your MongoDB Connection String>', { useNewUrlParser: true, useUnifiedTopology: true });

app.listen(3000, () => {
  console.log('Server is listening on port 3000');
});

前端代码与后端交互

在前端,我们需要将订阅信息发送到后端服务器以进行保存。

function sendSubscriptionToServer(subscription) {
  return fetch('/subscribe', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(subscription)
  }).then(response => {
    if (!response.ok) {
      throw new Error('Failed to send subscription to server');
    }
    return response.json();
  });
}

navigator.serviceWorker.ready.then(function(registration) {
  registration.pushManager.getSubscription().then(function(subscription) {
    if (!subscription) {
      // Subscribe user
      const applicationServerKey = urlBase64ToUint8Array('<Your Public VAPID Key>');
      registration.pushManager.subscribe({
        userVisibleOnly: true,
        applicationServerKey: applicationServerKey
      }).then(function(subscription) {
        console.log('User is subscribed:', subscription);
        return sendSubscriptionToServer(subscription);
      }).catch(function(error) {
        console.log('Failed to subscribe the user:', error);
      });
    } else {
      console.log('User is already subscribed:', subscription);
    }
  });
});

发送推送通知

在需要发送推送通知的时候,可以通过后端 API 来触发通知发送。

function sendNotification() {
  const payload = {
    title: '推送通知标题',
    body: '推送通知内容',
    icon: '/path/to/icon.png',
    badge: '/path/to/badge.png'
  };

  fetch('/sendNotification', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(payload)
  }).then(response => {
    if (!response.ok) {
      throw new Error('Failed to send notification');
    }
    return response.json();
  });
}

总结

通过本文的介绍,我们详细了解了如何在 PWA 中实现推送通知功能。从服务工作者的注册到订阅推送服务,再到处理推送事件,这一系列的步骤不仅使得应用更加互动,还提升了用户的参与度。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

乐闻x

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值