前言
在现代互联网应用中,推送通知是提升用户参与度和互动性的关键功能之一。对于进阶的网页应用(PWA),推送通知尤为重要,因为它可以让应用保持用户的关注,即使应用未被打开。本文将详细讲解如何在PWA中实现推送通知。
什么是 PWA
在开始之前,先简要介绍一下PWA。Progressive Web Apps(PWA)是一种能在网页和原生应用之间架起桥梁的技术,使得网页应用能够拥有类似原生应用的用户体验。PWA具有以下特点:
- 响应式设计
- 能够离线使用
- 可安装到设备主屏幕
- 提供推送通知
实现推送通知的步骤
实现推送通知主要分为以下几个步骤:
- 注册服务工作者
- 申请权限
- 订阅推送服务
- 处理推送事件
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 中实现推送通知功能。从服务工作者的注册到订阅推送服务,再到处理推送事件,这一系列的步骤不仅使得应用更加互动,还提升了用户的参与度。