如何处理React Native WebView内嵌Web应用的Firebase通知问题
问题原因
React Native WebView并非标准浏览器环境,FCM Web SDK依赖的Push API、Service Worker等Web核心API在WebView中未实现,直接使用Web版FCM SDK会触发不兼容报错。
解决方案
推荐通过React Native原生层处理FCM通知,这是最稳定可靠的方案,具体步骤如下:
步骤1:React Native端集成原生FCM SDK
- 安装依赖:
npm install @react-native-firebase/app @react-native-firebase/messaging - 按照Firebase官方文档完成iOS和Android的原生FCM环境配置(添加Google服务文件、配置通知权限等)
步骤2:React Native端与WebView通信传递令牌
修改WebView组件,获取原生FCM令牌并通过postMessage传给Web应用:
import React, { useEffect, useState, useRef } from 'react'; import { WebView } from 'react-native-webview'; import messaging from '@react-native-firebase/messaging'; const AppWebView = () => { const [fcmToken, setFcmToken] = useState(''); const webViewRef = useRef(null); useEffect(() => { async function fetchToken() { const authStatus = await messaging.requestPermission(); if (authStatus === messaging.AuthorizationStatus.AUTHORIZED) { const token = await messaging.getToken(); setFcmToken(token); } } fetchToken(); }, []); const handleWebMessage = (event) => { if (event.nativeEvent.data === 'GET_FCM_TOKEN') { webViewRef.current?.postMessage(JSON.stringify({ type: 'FCM_TOKEN', token: fcmToken })); } }; return ( <WebView ref={webViewRef} source={{ uri: '你的Web应用HTTPS地址' }} onMessage={handleWebMessage} injectedJavaScript={`window.ReactNativeWebView.postMessage('GET_FCM_TOKEN');`} allowUniversalAccessFromFileURLs={true} /> ); }; export default AppWebView;
步骤3:Web应用适配双环境
修改Web应用代码,判断当前环境是浏览器还是WebView,分别处理FCM令牌:
import { useEffect } from 'react'; import firebase from 'firebase/app'; import 'firebase/messaging'; import axios from 'axios'; const firebaseConfig = { apiKey: "AIzaSyC9K1X3mSSw2d9RTVqBwBRy7ECtmkzJ4to", authDomain: "brotherize-654cf.firebaseapp.com", projectId: "brotherize-654cf", storageBucket: "brotherize-654cf.appspot.com", messagingSenderId: "612370118030", appId: "1:612370118030:web:5a6bf0d77545330b2560df", measurementId: "G-4YSQ78M7VP", }; let currentFcmToken = null; // 判断是否处于React Native WebView中 const isWebViewEnv = () => typeof window.ReactNativeWebView !== 'undefined'; useEffect(() => { if (isWebViewEnv()) { // 监听来自原生的令牌消息 window.addEventListener('message', (event) => { try { const data = JSON.parse(event.data); if (data.type === 'FCM_TOKEN' && data.token) { currentFcmToken = data.token; saveTokenToLocalStorage(currentFcmToken); saveTokenToDb(currentFcmToken); } } catch (e) { console.error('解析WebView消息失败:', e); } }); } else { // 浏览器环境下用Web版FCM SDK firebase.initializeApp(firebaseConfig); const messaging = firebase.messaging(); requestNotificationPermission(messaging); } }, []); export const requestNotificationPermission = async (messaging) => { try { const permission = await Notification.requestPermission(); if (permission === 'granted') { console.log('通知权限已获取'); getDeviceToken(messaging); } else { console.log('通知权限被拒绝'); } } catch (error) { console.error('请求通知权限失败:', error); } }; const getDeviceToken = async (messaging) => { try { currentFcmToken = await messaging.getToken({ vapidKey: process.env.NEXT_PUBLIC_SERVER_KEY, }); if (currentFcmToken) { console.log('设备令牌:', currentFcmToken); saveTokenToLocalStorage(currentFcmToken); saveTokenToDb(currentFcmToken); } else { console.log('无可用注册令牌'); } } catch (error) { console.error('获取设备令牌失败:', error); } }; const saveTokenToLocalStorage = (token) => { localStorage.setItem('device_token', JSON.stringify(token)); console.log('令牌已保存到本地存储'); }; const saveTokenToDb = async (token) => { try { await axios.post('/v1/user/token', { deviceToken: token }); console.log('令牌已保存到数据库'); } catch (error) { console.error('保存令牌到数据库失败:', error); } };
不推荐的替代方案
如果坚持在WebView中使用Web版FCM,可尝试使用支持完整浏览器API的第三方WebView(如react-native-webview-chromium),但这类方案兼容性差、配置复杂,且无法保证在所有设备上正常工作,不建议用于生产环境。
内容的提问来源于stack exchange,提问作者Kamran Ali
相关产品推荐
相关产品推荐

