React中如何在public目录的firebase-messaging-sw.js中访问状态?
解决Firebase Service Worker访问React语言状态的问题
Service Worker(也就是你放在public目录的firebase-messaging-sw.js)运行在独立于主页面的上下文里,无法直接访问React的useContext状态——因为它没有DOM环境,也不参与React组件的渲染流程。不过可以通过以下几种间接方式实现需求:
方法一:将语言状态同步到本地存储(localStorage/IndexedDB)
在React的Context管理代码里,每当语言状态变更时,同步把值存到localStorage,Service Worker直接读取这个存储的值即可。
步骤1:React端同步语言状态到localStorage
在你的语言Context Provider组件中,添加同步逻辑:
import { createContext, useContext, useState, useEffect } from 'react'; const LanguageContext = createContext(); export function LanguageProvider({ children }) { const [language, setLanguage] = useState('zh-CN'); // 默认语言 // 语言变化时同步到localStorage useEffect(() => { localStorage.setItem('app_language', language); }, [language]); return ( <LanguageContext.Provider value={{ language, setLanguage }}> {children} </LanguageContext.Provider> ); } export const useLanguage = () => useContext(LanguageContext);
步骤2:Service Worker读取localStorage的值
修改你的firebase-messaging-sw.js,在处理后台消息时读取存储的语言:
importScripts("https://www.gstatic.com/firebasejs/9.0.2/firebase-app-compat.js"); importScripts("https://www.gstatic.com/firebasejs/9.0.2/firebase-messaging-compat.js"); const firebaseConfig = { apiKey: "", authDomain: "", projectId: "", storageBucket: "", messagingSenderId: "", appId: "", measurementId: "" }; firebase.initializeApp(firebaseConfig) const messaging = firebase.messaging(); const openDatabase = async () => { return new Promise((resolve, reject) => { const request = indexedDB.open('qapp-firebase-messages', 1); request.onerror = () => { console.error('Error opening IndexedDB database.'); reject(request.error); }; request.onsuccess = (event) => { resolve(request.result); }; request.onupgradeneeded = (event) => { const db = event.target.result; if (!db.objectStoreNames.contains('messages')) { const objectStore = db.createObjectStore('messages', { keyPath: 'id', autoIncrement: true }); } }; }); }; const storeMessage = async (messageData, language) => { const db = await openDatabase(); const transaction = db.transaction('messages', 'readwrite'); const objectStore = transaction.objectStore('messages'); // 把语言信息和消息一起存储 const request = objectStore.put({ ...messageData, language }); transaction.onerror = (error) => { console.error('Error storing message in IndexedDB:', error); }; }; messaging.onBackgroundMessage((payload) => { console.log("backgroundmessages recieved") console.log(payload.data) // 读取localStorage中的语言 const appLanguage = localStorage.getItem('app_language') || 'zh-CN'; storeMessage(payload.data, appLanguage); });
方法二:通过Client API向主页面请求语言状态
如果语言状态不适合存在本地存储(比如需要实时获取最新状态),可以让Service Worker向活跃的主页面客户端发送消息,请求当前语言:
步骤1:Service Worker中请求语言
修改firebase-messaging-sw.js的onBackgroundMessage:
messaging.onBackgroundMessage(async (payload) => { console.log("backgroundmessages recieved") console.log(payload.data) // 获取所有活跃的客户端 const clients = await self.clients.matchAll({ includeUncontrolled: true, type: 'window' }); if (clients.length > 0) { // 向第一个活跃客户端发送请求 const responsePromise = new Promise(resolve => { const listener = (event) => { if (event.data.type === 'LANGUAGE_RESPONSE') { self.removeEventListener('message', listener); resolve(event.data.language); } }; self.addEventListener('message', listener); clients[0].postMessage({ type: 'GET_LANGUAGE' }); }); const language = await responsePromise; storeMessage(payload.data, language); } else { // 没有活跃客户端时用默认语言 storeMessage(payload.data, 'zh-CN'); } });
步骤2:React页面监听消息并返回语言
在React的根组件或者语言Context Provider中添加监听:
useEffect(() => { const handleMessage = (event) => { if (event.data.type === 'GET_LANGUAGE') { // 向Service Worker返回当前语言 event.source.postMessage({ type: 'LANGUAGE_RESPONSE', language }); } }; navigator.serviceWorker.addEventListener('message', handleMessage); return () => { navigator.serviceWorker.removeEventListener('message', handleMessage); }; }, [language]);
方法三:推送时后端携带语言参数
如果你的后端存储了用户的语言偏好,可以在发送Firebase推送通知时,直接把语言信息放到payload的data字段里,Service Worker直接读取即可。这种方式不需要前端做额外同步,最直接可靠。
内容的提问来源于stack exchange,提问作者Nika
相关产品推荐
相关产品推荐

