多设备用户Web Push通知最优处理及对战答题App推送实现技术问询
Hey there! Let's tackle your two Web Push questions—they're both super relevant for real-world web apps, so I’ll share what I’ve learned from building similar systems.
1. Optimal Approach for Web Push with Multi-Device Users
When a user has multiple devices, the key is to balance reach, relevance, and avoiding annoyance. Here’s the best practice breakdown:
- Link Push Subscriptions to User Identities: First, your backend needs to map each user account to all their active push subscriptions (the
PushSubscriptionobjects generated when a user opts in). Every time a user logs into a new device and enables push, send the new subscription to your backend and associate it with their user ID. Don’t forget to clean up stale subscriptions: if you get a410 Goneresponse when sending a push, that subscription is invalid (user cleared browser data, unsubscribed, etc.)—remove it from the user’s device list immediately. - Target Notifications Strategically: Don’t blast the same notification to every device. Track which devices the user is actively using (via frontend heartbeats or the Page Visibility API, sent to your backend) and only send notifications to idle devices. Alternatively, let users set preferences (e.g., "only send game notifications to my phone") and honor those when triggering pushes.
- Sync Preferences Across Devices: If a user turns off a notification type on one device, update that preference across all their linked subscriptions in your backend. This ensures consistent behavior no matter which device they’re using.
- Avoid Duplicate Clutter: If you do need to send notifications to multiple devices, use the
tagfield in your push options. Notifications with the same tag will replace existing ones on a device, so you won’t end up with 5 identical alerts cluttering their notification center.
2. Web Push for Turn-Based 2-Player Quiz Apps
Let’s start with the optimal solution, then cover fallback options if you hit constraints.
Optimal Solution (Real-Time Push Triggers)
This relies on backend state management paired with Service Workers and the Push API:
- Track Game State on the Backend: Maintain a database record for each game that includes the current turn (user A or B) and both users’ active push subscriptions.
- Trigger Push on Turn Completion: When user A submits their answer, your frontend calls a backend API to update the game state to "user B’s turn." The backend immediately initiates a push to all valid subscriptions linked to user B.
- Craft a payload with context: Include game ID and action data so the notification can deep-link directly to the game. Example payload:
{ "title": "Your Turn to Answer!", "body": "Your opponent just finished their round—don’t keep them waiting!", "data": {"gameId": "quiz-game-123", "route": "/game/123"} }
- Craft a payload with context: Include game ID and action data so the notification can deep-link directly to the game. Example payload:
- Service Worker Handling: Use your service worker to display notifications and handle clicks:
// Listen for incoming push events self.addEventListener('push', (event) => { const payload = event.data?.json() || {}; const notificationOptions = { body: payload.body, data: payload.data, icon: '/quiz-app-icon.png', badge: '/notification-badge.png' }; event.waitUntil(self.registration.showNotification(payload.title, notificationOptions)); }); // Handle notification clicks to deep-link to the game self.addEventListener('notificationclick', (event) => { event.notification.close(); // Open the game page, or focus an existing tab if it's already open event.waitUntil( clients.matchAll({ type: 'window', includeUncontrolled: true }) .then((clientList) => { for (const client of clientList) { if (client.url.includes(event.notification.data.gameId) && 'focus' in client) { return client.focus(); } } if (clients.openWindow) { return clients.openWindow(event.notification.data.route); } }) ); }); - Skip Push for Active Users: Before sending a push, check if user B has an active session (via recent heartbeats). If they’re currently playing on any device, skip the push to avoid annoying them mid-game.
- Refresh Expired Subscriptions: Have your frontend listen for the
pushsubscriptionchangeevent—this fires when a subscription becomes invalid. When it does, re-subscribe the user and send the new subscription to your backend to update their device list.
Feasible Fallback Solutions
If real-time backend triggers aren’t feasible (e.g., limited backend resources, complex state management), try these:
- Background Polling via Service Worker: Use a service worker to periodically call your backend API (every 3–5 minutes, for example) to check if it’s the user’s turn. If it is, display a local notification directly from the service worker. This avoids the need for backend-initiated pushes but introduces some delay.
- Scheduled Backend Scans: Instead of triggering pushes in real-time, have your backend run a periodic job (every 1 minute) that scans all games waiting for a user’s turn. For each pending game, send a push to the waiting user. This reduces backend complexity compared to real-time triggers, with minimal delay.
- WebRTC Peer-to-Peer (Limited Use Case): If both users are online, use WebRTC to establish a direct connection. When one user finishes their turn, send a message directly to the other user’s browser to trigger a local notification. This works great for active sessions but fails if either user is offline, so it’s best used as a supplement to push, not a replacement.
内容的提问来源于stack exchange,提问作者Sebastian
相关产品推荐
相关产品推荐

