You need to enable JavaScript to run this app. React聊天PWA集成FCM实现按需触发新消息推送的问题咨询
优惠活动
大模型
产品
解决方案
定价
更多

React聊天PWA集成FCM实现按需触发新消息推送的问题咨询

实现方案

1. 前端页面活跃状态检测

核心通过监听浏览器事件判断用户是否在当前应用页面,状态同步到本地存储供后续逻辑使用

import { useEffect } from 'react';

const App = () => {
  useEffect(() => {
    const updateActiveStatus = () => {
      // 标签页未隐藏 + 窗口处于激活状态才判定为用户在应用内
      const isAppActive = !document.hidden && document.hasFocus();
      localStorage.setItem('app_is_active', isAppActive ? '1' : '0');
      // 可选:将状态同步到后端,用于消息已读标记逻辑
    };

    // 绑定状态监听事件
    document.addEventListener('visibilitychange', updateActiveStatus);
    window.addEventListener('focus', updateActiveStatus);
    window.addEventListener('blur', updateActiveStatus);

    // 初始化状态
    updateActiveStatus();

    return () => {
      // 组件销毁时解绑事件
      document.removeEventListener('visibilitychange', updateActiveStatus);
      window.removeEventListener('focus', updateActiveStatus);
      window.removeEventListener('blur', updateActiveStatus);
    };
  }, []);

  return <>{/* 应用其余逻辑 */}</>
};
export default App;

2. React端FCM接入逻辑

首先安装依赖:npm install firebase @firebase/messaging

FCM初始化封装

// src/firebase.js
import { initializeApp } from "firebase/app";
import { getMessaging, getToken, onMessage } from "firebase/messaging";

const firebaseConfig = {
  // 替换为你自己的Firebase项目配置
  apiKey: "YOUR_API_KEY",
  authDomain: "YOUR_AUTH_DOMAIN",
  projectId: "YOUR_PROJECT_ID",
  storageBucket: "YOUR_STORAGE_BUCKET",
  messagingSenderId: "YOUR_SENDER_ID",
  appId: "YOUR_APP_ID"
};

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

// 获取FCM Token并同步到后端
export const initFcm = async () => {
  // 先申请通知权限
  const permission = await Notification.requestPermission();
  if (permission !== 'granted') return;

  try {
    const currentToken = await getToken(messaging, {
      vapidKey: "YOUR_VAPID_PUBLIC_KEY",
      serviceWorkerRegistration: await navigator.serviceWorker.register('/firebase-messaging-sw.js')
    });
    if (currentToken) {
      // 上传Token到Laravel后端
      await fetch('/api/user/fcm-token', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        credentials: 'include',
        body: JSON.stringify({ token: currentToken })
      });
    }
  } catch (e) {
    console.error('FCM初始化失败', e);
  }
};

// 监听前台推送消息
export const listenForegroundMsg = (onReceiveMsg) => {
  onMessage(messaging, (payload) => {
    const isAppActive = localStorage.getItem('app_is_active') === '1';
    if (isAppActive) {
      // 应用在前台时仅走内部消息更新,不弹系统通知
      onReceiveMsg(payload);
    } else {
      // 应用不在前台时手动弹系统通知
      new Notification(payload.notification.title, {
        body: payload.notification.body,
        icon: '/icons/notification-icon.png',
        data: payload.data
      });
    }
  });
};

Service Worker配置

在项目public目录下新建firebase-messaging-sw.js,用于处理后台推送:

// 可将对应Firebase JS文件下载到本地托管,避免引用外部资源
importScripts('/static/firebase-app-compat.js');
importScripts('/static/firebase-messaging-compat.js');

firebase.initializeApp({
  // 和前端一致的Firebase配置
  apiKey: "YOUR_API_KEY",
  authDomain: "YOUR_AUTH_DOMAIN",
  projectId: "YOUR_PROJECT_ID",
  storageBucket: "YOUR_STORAGE_BUCKET",
  messagingSenderId: "YOUR_SENDER_ID",
  appId: "YOUR_APP_ID"
});

const messaging = firebase.messaging();

// 处理后台/应用关闭时的推送
messaging.onBackgroundMessage((payload) => {
  const options = {
    body: payload.notification.body,
    icon: '/icons/notification-icon.png',
    data: payload.data
  };
  self.registration.showNotification(payload.notification.title, options);
});

// 监听通知点击事件,跳转到对应聊天页面
self.addEventListener('notificationclick', (event) => {
  event.notification.close();
  event.waitUntil(
    clients.openWindow(`/chat/${event.notification.data.sender_id}`)
  );
});

3. Laravel后端FCM对接

首先安装Firebase PHP SDK:composer require kreait/firebase-php

配置Firebase

将Firebase控制台下载的服务账号JSON文件放到storage/firebase-service-account.json,在config/services.php中新增配置:

'firebase' => [
    'credentials' => storage_path('firebase-service-account.json'),
],

相关接口实现

// routes/api.php
Route::middleware('auth:sanctum')->group(function () {
    // 保存前端上传的FCM Token
    Route::post('/user/fcm-token', function (Request $request) {
        $request->validate(['token' => 'required|string']);
        $request->user()->update(['fcm_token' => $request->token]);
        return response()->json(['status' => 'ok']);
    });

    // 发送消息接口
    Route::post('/message/send', [\App\Http\Controllers\MessageController::class, 'send']);
});
// app/Http/Controllers/MessageController.php
namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\Message;
use Kreait\Firebase\Messaging\CloudMessage;
use Kreait\Firebase\Messaging\Notification;

class MessageController extends Controller
{
    public function send(Request $request)
    {
        $request->validate([
            'receiver_id' => 'required|exists:users,id',
            'content' => 'required|string'
        ]);

        // 保存消息到数据库
        $message = Message::create([
            'sender_id' => auth()->id(),
            'receiver_id' => $request->receiver_id,
            'content' => $request->content
        ]);

        // 给接收方发推送
        $receiver = \App\Models\User::find($request->receiver_id);
        if ($receiver->fcm_token) {
            $notification = Notification::create(auth()->user()->name, $request->content);
            $fcmMsg = CloudMessage::withTarget('token', $receiver->fcm_token)
                ->withNotification($notification)
                ->withData([
                    'sender_id' => auth()->id(),
                    'message_id' => $message->id
                ]);
            app('firebase.messaging')->send($fcmMsg);
        }

        return response()->json($message);
    }
}

注意事项

  • 每次应用启动时都要校验FCM Token是否更新,及时同步到后端
  • 该方案仅适用于支持FCM的地区,国内环境需要替换为对应的推送服务商
  • 用户表需要新增fcm_token字段存储用户设备的推送令牌

内容的提问来源于stack exchange,提问作者Nazmul Hossain

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.17 01:24:10