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

Next.js Web应用FCM通知接收异常(Android端正常)求助

Next.js 12.3.1 + FCM 前台消息不触发onMessage问题解决

问题核心

服务端通过Firebase Admin SDK能正常给Android设备发通知,但Web端仅能弹出系统通知弹窗,无法触发onMessage函数(无日志输出),也无法更新React状态、展示应用内消息组件。已尝试切换Firebase版本、启用HTTPS、调整配置,均无改善。

问题分析

  1. Next.js SSR环境限制:模块级代码可能在服务器端执行,或组件未挂载就完成onMessage注册,无法关联React组件状态。
  2. Service Worker配置不一致:客户端与Service Worker的Firebase版本不匹配,或文件路径/初始化逻辑错误。
  3. 消息监听时机错误:onMessage未在React组件生命周期内注册,无法捕获前台消息。
  4. Payload格式不符合Web端要求:服务端发送的消息未满足FCM Web前台消息的格式规范。

解决方案

1. 重构Firebase工具函数,组件内注册消息监听

将FCM初始化逻辑封装为工具函数,在React组件的useEffect中完成监听注册,确保在浏览器环境且组件挂载后执行:

工具函数文件(utils/firebase.js)

import { initializeApp } from 'firebase/app';
import { getMessaging, getToken, onMessage } from 'firebase/messaging';

const firebaseConfig = {
  // 你的Firebase配置信息
};

let app;
let messaging;

export const getFirebaseApp = () => {
  if (!app && typeof window !== 'undefined') {
    app = initializeApp(firebaseConfig);
  }
  return app;
};

export const getFirebaseMessaging = () => {
  if (!messaging && typeof window !== 'undefined') {
    const app = getFirebaseApp();
    messaging = getMessaging(app);
  }
  return messaging;
};

export const generateFirebaseToken = async () => {
  const permission = await Notification.requestPermission();
  if (permission !== 'granted') throw new Error('通知权限未授予');
  
  const messaging = getFirebaseMessaging();
  const token = await getToken(messaging, {
    vapidKey: process.env.NEXT_PUBLIC_FIREBASE_VAPID_KEY, // 必须用NEXT_PUBLIC_前缀暴露给客户端
  });
  return token;
};

export const setupMessageListener = (callback) => {
  const messaging = getFirebaseMessaging();
  return onMessage(messaging, callback);
};

组件内使用(components/NotificationCenter.js)

import { useState, useEffect } from 'react';
import { generateFirebaseToken, setupMessageListener } from '../utils/firebase';

export default function NotificationCenter() {
  const [messages, setMessages] = useState([]);

  useEffect(() => {
    // 初始化FCM并获取Token
    const initFCM = async () => {
      try {
        const token = await generateFirebaseToken();
        console.log('FCM Token:', token);
        // 可选:将Token发送到服务端存储
        // await fetch('/api/save-fcm-token', { method: 'POST', body: JSON.stringify({ token }) });
      } catch (err) {
        console.error('FCM初始化失败:', err);
      }
    };

    // 注册前台消息监听
    const unsubscribe = setupMessageListener((payload) => {
      console.log('收到前台消息:', payload);
      // 更新React状态,渲染消息列表
      setMessages(prev => [...prev, payload]);
    });

    initFCM();

    // 组件卸载时取消监听
    return () => unsubscribe();
  }, []);

  return (
    <div className="notification-center">
      <h3>应用内通知</h3>
      {messages.length === 0 ? (
        <p>暂无新通知</p>
      ) : (
        messages.map((msg, idx) => (
          <div key={idx} className="notification-item">
            <h4>{msg.notification.title}</h4>
            <p>{msg.notification.body}</p>
            {msg.data?.customData && <p>自定义数据:{msg.data.customData}</p>}
          </div>
        ))
      )}
    </div>
  );
}

2. 修复Service Worker配置

  • 文件位置:确保firebase-messaging-sw.js放在Next.js的public目录下,保证静态资源可访问。
  • 版本同步:客户端与Service Worker使用相同版本的Firebase SDK,示例如下:
// public/firebase-messaging-sw.js
importScripts('https://www.gstatic.com/firebasejs/10.7.1/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/10.7.1/firebase-messaging.js');

const firebaseConfig = {
  // 你的Firebase配置信息
};

firebase.initializeApp(firebaseConfig);

const messaging = firebase.messaging();

// 后台消息处理(应用在后台时触发)
messaging.onBackgroundMessage((payload) => {
  console.log('[Service Worker] 收到后台消息:', payload);
  // 自定义后台通知弹窗(可选)
  const notificationTitle = payload.notification.title;
  const notificationOptions = {
    body: payload.notification.body,
    icon: '/firebase-icon.png' // 替换为你的图标路径
  };
  self.registration.showNotification(notificationTitle, notificationOptions);
});

3. 正确注册Service Worker

在_app.js中通过useEffect注册Service Worker,替代原SWR的错误方式:

// pages/_app.js
import { useEffect } from 'react';

export default function MyApp({ Component, pageProps }) {
  useEffect(() => {
    if ('serviceWorker' in navigator) {
      navigator.serviceWorker.register('/firebase-messaging-sw.js')
        .then(registration => {
          console.log('Service Worker注册成功:', registration);
        })
        .catch(err => {
          console.error('Service Worker注册失败:', err);
        });
    }
  }, []);

  return <Component {...pageProps} />;
}

4. 规范服务端消息Payload格式

确保服务端发送的消息包含notification字段(用于系统弹窗)和data字段(用于自定义数据),示例:

// 服务端Firebase Admin SDK发送示例
const message = {
  token: webClientToken,
  notification: {
    title: '新消息提醒',
    body: '你收到一条新的系统通知'
  },
  data: {
    type: 'system',
    id: '12345'
  },
  webpush: {
    fcmOptions: {
      link: 'https://your-app-domain.com/notification' // 点击通知跳转路径
    }
  }
};

await admin.messaging().send(message);

额外排查点

  • 确认NEXT_PUBLIC_FIREBASE_VAPID_KEY环境变量配置正确,且能被客户端读取。
  • 在浏览器控制台Application > Service Workers中检查Service Worker状态,确保无报错、处于激活状态。
  • 查看Application > Push Messaging,确认FCM订阅状态正常。

内容的提问来源于stack exchange,提问作者Nima Baghi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 15:53:16