You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React项目中结合.env环境变量使用Firebase Cloud Messaging Service Worker时遇到订阅失败问题

React项目中结合.env环境变量使用Firebase Cloud Messaging Service Worker时遇到订阅失败问题

看起来你在尝试给React项目的FCM Service Worker传递环境变量时碰到了订阅失败的问题,我来帮你梳理下问题根源和解决办法:

问题分析

你当前的实现有两个关键问题导致了这个报错:

  1. 消息发送时机不对:在Service Worker注册完成后直接用registration.active.postMessage,但此时Service Worker可能还处于installing或waiting状态,并没有真正激活,registration.active可能是null,导致环境变量根本没传递到SW中。
  2. SW初始化逻辑有问题:把Firebase初始化和后台消息监听放在message事件回调里,会导致每次收到消息都尝试重新初始化App,重复调用initializeApp会报错,而且如果第一次消息没成功传递,SW就完全没初始化FCM,自然无法完成Push订阅。

解决方案

1. 修正Service Worker注册与消息传递时机

不要在注册完成后立即发送消息,而是等待Service Worker完全激活(通过navigator.serviceWorker.ready事件),确保SW处于活跃状态再传递环境变量:

修改src/index.js的代码:

// src/index.js
if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/firebase-messaging-sw.js', {
    scope: '/'
  })
  .then(async function(registration) {
    console.log('Registration successful, scope is:', registration.scope);
    // 等待Service Worker完全激活
    const swRegistration = await navigator.serviceWorker.ready;
    // 此时swRegistration.active一定存在,安全发送消息
    swRegistration.active.postMessage({
      apiKey: process.env.REACT_APP_FIREBASE_ANALYTICS_API_KEY,
      authDomain: process.env.REACT_APP_FIREBASE_ANALYTICS_AUTH_DOMAIN,
      projectId: process.env.REACT_APP_FIREBASE_ANALYTICS_PROJECT_ID,
      storageBucket: process.env.REACT_APP_FIREBASE_ANALYTICS_STORAGE_BUCKET,
      messagingSenderId: process.env.REACT_APP_FIREBASE_ANALYTICS_MESSAGING_SENDER_ID,
      appId: process.env.REACT_APP_FIREBASE_ANALYTICS_APP_ID,
      measurementId: process.env.REACT_APP_FIREBASE_ANALYTICS_MEASUREMENT_ID,
    });
  })
  .catch(function(err) {
    console.log('Service worker registration failed, error:', err);
  });
}

2. 优化Firebase Service Worker的初始化逻辑

避免重复初始化Firebase App,只在第一次收到环境变量时初始化一次,并把后台消息监听放在初始化之后:

修改firebase-messaging-sw.js:

// firebase-messaging-sw.js
importScripts('https://www.gstatic.com/firebasejs/8.10.1/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/8.10.1/firebase-messaging.js');

// 全局保存messaging实例,避免重复初始化
let messagingInstance;

self.addEventListener('message', (event) => {
  const {
    apiKey,
    authDomain,
    projectId,
    storageBucket,
    messagingSenderId,
    appId,
    measurementId
  } = event.data;

  // 仅当Firebase App未初始化时才执行初始化
  if (!firebase.apps.length) {
    firebase.initializeApp({
      apiKey,
      authDomain,
      projectId,
      storageBucket,
      messagingSenderId,
      appId,
      measurementId,
      // databaseURL: 'https://project-id.firebaseio.com', // Uncomment if needed
    });

    // 初始化messaging并监听后台消息
    messagingInstance = firebase.messaging();
    messagingInstance.onBackgroundMessage((payload) => {
      console.log('[firebase-messaging-sw.js] Received background message ', payload);

      // Customize notification here
      const notificationTitle = payload.notification.title;
      const notificationOptions = {
        body: payload.notification.body,
        icon: payload.notification.image
      };

      self.registration.showNotification(notificationTitle, notificationOptions);
    });
  }
});

3. 确保客户端与SW的Firebase配置一致

在src/App.js中初始化Firebase时也要使用相同的环境变量,保证客户端和SW的配置统一,避免token生成异常:

// src/App.js
import { useEffect } from 'react';
import { initializeApp } from "firebase/app";
import { getMessaging, getToken, onMessage } from "firebase/messaging";
import { toast } from 'your-toast-library'; // 替换成你实际使用的toast库

// 用环境变量初始化Firebase
const firebaseConfig = {
  apiKey: process.env.REACT_APP_FIREBASE_ANALYTICS_API_KEY,
  authDomain: process.env.REACT_APP_FIREBASE_ANALYTICS_AUTH_DOMAIN,
  projectId: process.env.REACT_APP_FIREBASE_ANALYTICS_PROJECT_ID,
  storageBucket: process.env.REACT_APP_FIREBASE_ANALYTICS_STORAGE_BUCKET,
  messagingSenderId: process.env.REACT_APP_FIREBASE_ANALYTICS_MESSAGING_SENDER_ID,
  appId: process.env.REACT_APP_FIREBASE_ANALYTICS_APP_ID,
  measurementId: process.env.REACT_APP_FIREBASE_ANALYTICS_MEASUREMENT_ID,
};

const app = initializeApp(firebaseConfig);
const messaging = getMessaging(app);

const generateCloudMessagingToken = async () => {
  try {
    const currentToken = await getToken(messaging, {
      vapidKey: process.env.REACT_APP_FIREBASE_VAPID_KEY // 如果需要VAPID密钥,也从env获取
    });
    if (currentToken) {
      console.log('FCM Token generated:', currentToken);
      // 这里可以将token发送到后端保存
    } else {
      console.log('No token found, please request notification permission first.');
    }
  } catch (err) {
    console.error('Error generating FCM token:', err);
  }
};

function App() {
  useEffect(() => {
    navigator.serviceWorker.ready.then(() => {
      if (Notification.permission === "granted") {
        generateCloudMessagingToken();
        onMessage(messaging, (payload) => {
          console.log("Foreground message received: ", payload);
          toast.success(payload.notification.title);
        });
      } else {
        // 可以在这里请求通知权限
        Notification.requestPermission().then(permission => {
          if (permission === "granted") {
            generateCloudMessagingToken();
          }
        });
      }
    });
  }, []);

  // ... rest of your App component
}

export default App;

调试验证步骤

  1. 打开浏览器开发者工具的Application -> Service Workers面板,确认SW处于Activated状态,没有报错。
  2. 在Console中查看是否有Service Worker注册成功、消息发送成功的日志。
  3. 检查.env文件中的变量是否都以REACT_APP_开头(React仅会加载带这个前缀的环境变量)。

备注:内容来源于stack exchange,提问作者Ankit Agrawal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.17 12:13:08