使用Firebase getToken时提示无活跃Service Worker问题求助
initializing Firebase Messaging: DOMException: Failed to execute 'subscribe' on 'PushManager': Subscription failed - no active Service Worker
我通过以下代码确认Firebase Service Worker已激活:
const regs = await navigator.serviceWorker.getRegistrations(); console.log("🚀 ~ $$$ ~ requestNotificationPermission ~ regs:", regs)
但仍出现上述错误。我的Firebase配置从后端获取,相关代码如下,求解决思路。
初始化Service Worker的推送通知组件代码
import { useCallback, useEffect } from 'react'; import { useQuery } from '@apollo/client'; import { requestNotificationPermission } from 'app/utils/firebaseUtils'; import { notify } from 'app/utils/notification/notification'; import { initializeApp } from 'firebase/app'; import { getMessaging, onMessage } from 'firebase/messaging'; import { firebaseConfigQuery } from 'graphql/app/pushNotificationQueries'; const PushNotifications = () => { const { data } = useQuery(firebaseConfigQuery); const intiFirebaseApp = useCallback(async(config) =>{ if(!config) return; const { vapidKey, ...appConfig } = config; const app = initializeApp(appConfig); const messaging = getMessaging(app); const permission = await requestNotificationPermission(messaging, vapidKey); if(permission === 'granted') { onMessage(messaging, payload => { const { topic, payload:message } = payload?.data; console.log('🚀 ~ $$$ ~ PushNotifications ~ payload:', payload); // 用户登录并订阅通知时无需显示通知 if(topic === 'subscription') return; notify(topic, {body: message}); }); } },[]); const initFirebaseSW = useCallback(async (config) => { if ('serviceWorker' in navigator) { try{ const registration = await navigator.serviceWorker.register('/firebase-messaging-sw.js', { scope: '/' }); console.log('Service Worker registered with scope:', registration?.scope); await navigator.serviceWorker.ready; // await registration?.pushManager?.subscribe({ // userVisibleOnly:true, // }); intiFirebaseApp(config); }catch(err){ console.error('Service Worker registration failed:', err); } } }, [intiFirebaseApp]); useEffect(()=>{ const firebaseConfig = data?.affectliSettings?.messaging; if(firebaseConfig){ initFirebaseSW(firebaseConfig); } },[data, initFirebaseSW]); return null; }; export default PushNotifications;
请求通知权限及订阅工具函数代码
export const requestNotificationPermission = async (messaging, vapidKey) => { if (!('Notification' in window)) { //eslint-disable-next-line console.log('This browser does not support notifications.'); return Promise.resolve(); } // 检查当前权限状态 let permission = Notification.permission; // 如果权限是'default',则请求权限 if (permission === 'default') { permission = await Notification.requestPermission(); } if (permission === 'granted') { const regs = await navigator.serviceWorker.getRegistrations(); console.log("🚀 ~ $$$ ~ requestNotificationPermission ~ regs:", regs) const currentToken = await getToken(messaging, {vapidKey}); console.log('🚀 ~ $$$ ~ requestNotificationPermission ~ currentToken:', currentToken); if(currentToken){ // 将token发送到服务器 const res = await subscribeNotifications(currentToken); const status = res?.data?.pushNotificationAddToken; if(typeof status === 'boolean'){ if(status){ //eslint-disable-next-line console.log('$$$ : 推送通知订阅成功'); }else{ //eslint-disable-next-line console.log('$$$ : 推送通知已订阅'); } } } } else if (permission === 'denied') { // eslint-disable-next-line console.log('🚀 ~ requestNotificationPermission ~ permission:', permission); } return permission; };
解决思路
确保Service Worker完全激活后再初始化Firebase Messaging
虽然调用了await navigator.serviceWorker.ready,但Firebase的getToken可能需要等待Service Worker完成激活的全部步骤。可以修改initFirebaseSW,确认registration处于active状态后再执行初始化:await navigator.serviceWorker.ready; // 等待Service Worker激活 if (registration.active) { intiFirebaseApp(config); } else { registration.addEventListener('activate', () => { intiFirebaseApp(config); }); }检查
firebase-messaging-sw.js的位置与内容- 确保文件放在项目根目录(与
index.html同层级),否则注册路径会出错。 - 文件必须包含Firebase初始化代码,示例:
importScripts('https://www.gstatic.com/firebasejs/9.23.0/firebase-app-compat.js'); importScripts('https://www.gstatic.com/firebasejs/9.23.0/firebase-messaging-compat.js'); firebase.initializeApp({ apiKey: "YOUR_API_KEY", authDomain: "YOUR_AUTH_DOMAIN", projectId: "YOUR_PROJECT_ID", storageBucket: "YOUR_STORAGE_BUCKET", messagingSenderId: "YOUR_MESSAGING_SENDER_ID", appId: "YOUR_APP_ID" }); const messaging = firebase.messaging(); // 处理后台消息 messaging.onBackgroundMessage(function(payload) { console.log('[firebase-messaging-sw.js] Received background message ', payload); const notificationTitle = payload.notification.title; const notificationOptions = { body: payload.notification.body, icon: '/firebase-logo.png' }; self.registration.showNotification(notificationTitle, notificationOptions); });
- 确保文件放在项目根目录(与
清除存储后先注销旧Service Worker再重新注册
清除存储后旧的Service Worker可能处于不稳定状态,可在initFirebaseSW中先注销旧注册:// 先注销所有旧的Service Worker const registrations = await navigator.serviceWorker.getRegistrations(); for (let reg of registrations) { await reg.unregister(); } // 再注册新的 const registration = await navigator.serviceWorker.register('/firebase-messaging-sw.js', { scope: '/' });显式传入Service Worker注册给
getToken
在requestNotificationPermission中调用getToken时,显式传入当前的Service Worker注册,避免Firebase找不到正确的Worker:const registration = await navigator.serviceWorker.ready; const currentToken = await getToken(messaging, { vapidKey, serviceWorkerRegistration: registration });检查浏览器缓存与Service Worker更新策略
清除存储后浏览器可能缓存旧的Service Worker文件,开发环境可开启强制更新检查,生产环境需设置正确的缓存策略,确保新Worker能被正确加载。
内容的提问来源于stack exchange,提问作者Ali Nawaz

