如何实现客户端未访问网站时的消息推送?Service Worker相关咨询
Great question! Let’s break down how to implement offline push notifications similar to OneSignal, plus cover the general methods for sending messages when users aren’t actively visiting your site.
Your current Service Worker setup only works when the user is on your site because it relies on a direct connection to your server. Tools like OneSignal leverage the Web Push API + Service Workers + browser-managed push services to enable background notifications. Here’s how to build this:
1. Ensure HTTPS (or localhost for development)
The Web Push API requires a secure context. Your site must use HTTPS in production—localhost is exempt for testing purposes.
2. Get User Push Subscriptions
First, you need to ask the user for notification permissions, then get a unique PushSubscription object from their browser. This object includes an endpoint URL for the browser’s native push service (e.g., FCM for Chrome, Mozilla’s service for Firefox), which your backend will use to send push requests.
Example client-side code:
if ('serviceWorker' in navigator && 'PushManager' in window) { window.addEventListener('load', async () => { try { // Register your Service Worker const swRegistration = await navigator.serviceWorker.register('/sw.js'); // Request notification permission const permission = await Notification.requestPermission(); if (permission !== 'granted') { throw new Error('User denied notification permission'); } // Get push subscription (use your VAPID public key) const subscription = await swRegistration.pushManager.subscribe({ userVisibleOnly: true, // Browsers require pushes to show a notification applicationServerKey: urlBase64ToUint8Array('YOUR_VAPID_PUBLIC_KEY') }); // Save the subscription to your backend database await fetch('/api/subscribe', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(subscription) }); } catch (err) { console.error('Push setup failed:', err); } }); } // Helper: Convert VAPID public key from base64 to Uint8Array function urlBase64ToUint8Array(base64String) { const padding = '='.repeat((4 - base64String.length % 4) % 4); const base64 = (base64String + padding).replace(/-/g, '+').replace(/_/g, '/'); const rawData = window.atob(base64); return Uint8Array.from([...rawData].map(char => char.charCodeAt(0))); }
3. Set Up VAPID Keys
VAPID (Voluntary Application Server Identification) lets browser push services verify that your backend is a legitimate sender. Generate a pair of public/private keys using the web-push Node.js package:
npx web-push generate-vapid-keys
Store the private key securely on your backend—never expose it to clients. The public key is used in the client-side subscription code above.
4. Send Push Notifications from Your Backend
When you want to send a notification, your backend will send an HTTP request to the endpoint from the saved PushSubscription, signed with your VAPID private key.
Example backend code (Node.js with web-push):
const webpush = require('web-push'); // Configure VAPID credentials const vapidKeys = { publicKey: 'YOUR_VAPID_PUBLIC_KEY', privateKey: 'YOUR_VAPID_PRIVATE_KEY' }; webpush.setVapidDetails( 'mailto:your-contact-email@example.com', vapidKeys.publicKey, vapidKeys.privateKey ); // Send a push notification to a specific subscription async function sendPush(subscription, payload) { try { await webpush.sendNotification(subscription, JSON.stringify(payload)); console.log('Push sent successfully'); } catch (err) { console.error('Push failed:', err); // Remove invalid subscriptions (e.g., user cleared browser data) if (err.statusCode === 410) { await fetch('/api/unsubscribe', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(subscription) }); } } } // Example payload const notificationPayload = { title: 'Offline Alert!', body: 'You got this even without opening the site.', icon: '/icons/icon-192x192.png', url: '/new-update' // URL to open when notification is clicked }; // Send to a saved subscription sendPush(savedUserSubscription, notificationPayload);
5. Handle Push Events in Service Worker
Your Service Worker needs to listen for incoming push events and display the notification to the user. Add this to sw.js:
// Listen for push events self.addEventListener('push', (event) => { const payload = event.data?.json() || { title: 'New Notification' }; const options = { body: payload.body, icon: payload.icon, badge: '/icons/badge-72x72.png', data: { url: payload.url || '/' } }; // Show the notification event.waitUntil( self.registration.showNotification(payload.title, options) ); }); // Handle notification clicks self.addEventListener('notificationclick', (event) => { event.notification.close(); // Open the target URL in a new tab/window event.waitUntil( clients.openWindow(event.notification.data.url) ); });
Beyond the Web Push API (the standard web solution), here are other approaches depending on your platform:
1. Web Push API (Recommended for Web)
- Supports Chrome, Firefox, Edge, and Safari 16.4+
- Works as long as the browser is running (even in the background)
- Fully standards-based, no third-party dependency required
2. Native App Push Services
If you have a native app, use platform-specific push services for maximum reliability:
- iOS: Apple Push Notification Service (APNS)
- Android: Firebase Cloud Messaging (FCM)
- Windows: Windows Push Notification Service (WNS)
These are system-level services, so notifications reach users even if the app isn’t running.
3. Legacy Safari Push (Pre-16.4)
Older Safari versions don’t support Web Push. You’ll need to use Apple’s proprietary push system, which requires an Apple Developer account and push certificates. This is becoming obsolete as Safari 16.4+ gains traction.
4. Email/SMS Notifications
If you have user contact info, email or SMS are universal options. They don’t require browsers or apps, but lack real-time delivery and can be marked as spam.
- User Permissions: Users must explicitly allow notifications—never spam them.
- Subscription Expiry: Subscriptions can become invalid (e.g., user clears browser data). Always handle failed pushes and clean up invalid entries in your database.
- Background Limits: Mobile browsers may restrict background Service Worker activity, leading to delayed notifications. For critical alerts, pair web push with native app push if possible.
内容的提问来源于stack exchange,提问作者Đỗ Nguyễn

