Firebase - 讓React(web)也可以接收訊息推播
2024-10-17 03:00
前言
這幾天和朋友聊到手機App的推播,讓我聯想到Web端是否也可以像App一樣有接收推播的功能,當下越想越興奮,於是就誕生出了這篇文章。
今日目標
在部落格上實作訂閱功能
要達成這個功能除了使用 Firebase Cloud Messaging 外,還需要再專案內加入PWA和註冊Service Worker,原因之後會提到,PWA的介紹和教學在這裡。
P.S 由於這篇文章程式碼比較多,各位請慢慢看才不會漏掉小細節倒致無法成功。
註冊Firebase
- 前往Firebase點擊右上方的 Go to console,註冊並登入。
- 點選建立專案,輸入專案名稱後,點選建立。
- 進入
專案設定 -> 一般設定 -> 點選 </> 的圖標
來建立網頁應用。 - 完成後,會得到
firebaseConfig
這樣就完成註冊了。
在專案內加入PWA
由於我的Blog是使用Nextjs,PWA的配置方法會略有不同:
npm i next-pwa
- 修改
next.config.mjs
javascript1import withPWA from "next-pwa"; 2 3const nextConfig = { 4 output: "export", 5 pwa: { 6 dest: "public", 7 register: true, 8 skipWaiting: true, 9 }, 10}; 11 12export default nextConfig;
- public下新增
manifest.json
和一張 192x192的圖片
json1{ 2 "name": "Arthur Blog", 3 "short_name": "A'Blog", 4 "start_url": "/", 5 "display": "standalone", 6 "background_color": "#ffffff", 7 "theme_color": "#000000", 8 "icons": [ 9 { 10 "src": "/logo192.png", 11 "sizes": "192x192", 12 "type": "image/png" 13 } 14 ] 15}
- 修改
app/layout.tsx
tsx1... 2<html lang="en"> 3 <head> 4 <link rel="manifest" href="/manifest.json" /> 5 <link rel="icon" href="%PUBLIC_URL%/favicon.ico" /> 6 <meta name="viewport" content="width=device-width, initial-scale=1" /> 7 <meta name="theme-color" content="#000000" /> 8 <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" /> 9 <meta name="apple-mobile-web-app-capable" content="yes" /> 10 <meta name="apple-mobile-web-app-status-bar-style" content="black-translucen" /> 11 </head> 12 <body> 13 ... 14 </body> 15</html> 16...
註冊Service Worker
- public下新增
sw.js
javascript1self.addEventListener("install", (event) => { 2 event.waitUntil( 3 caches.open("my-pwa-cache").then((cache) => { 4 return cache.addAll(["/", "/index.html", "/icon-192x192.png"]); 5 }) 6 ); 7}); 8 9self.addEventListener("fetch", (event) => { 10 event.respondWith( 11 caches.match(event.request).then((response) => { 12 return response || fetch(event.request); 13 }) 14 ); 15});
- 修改
app/page.tsx
tsx1... 2useEffect(() => { 3 if (typeof navigator !== "undefined") { 4 if (typeof window !== "undefined" && "serviceWorker" in navigator) { 5 navigator.serviceWorker 6 .register("/sw.js") 7 .then((registration) => { 8 console.log( 9 "Service Worker registered with scope:", 10 registration.scope 11 ); 12 }) 13 .catch((error) => { 14 console.error("Service Worker registration failed:", error); 15 }); 16 } 17 } 18 }, []); 19...
- 確認是否完成PWA和Service Worker: 查看網址最右邊是否有出現
安裝
且打開F12 -> Application
查看Service Workers是否有註冊成功。
讓Web可以接收FireBase訊息
接下來我們要讓專案可以接收到推播的訊息:
npm i firebase
- public下新增
firebase-messaging-sw.js
並將剛註冊的firebaseConfig
貼上
javascript1importScripts( 2 "https://www.gstatic.com/firebasejs/9.0.0/firebase-app-compat.js" 3); 4importScripts( 5 "https://www.gstatic.com/firebasejs/9.0.0/firebase-messaging-compat.js" 6); 7 8firebase.initializeApp({ 9 //你的firebaseConfig 10}); 11 12const messaging = firebase.messaging(); 13 14messaging.onBackgroundMessage((payload) => { 15 const notificationTitle = payload.notification.title; 16 const notificationOptions = { 17 body: payload.notification.body, 18 icon: payload.notification.icon, 19 }; 20 21 self.registration.showNotification(notificationTitle, notificationOptions); 22});
- 接下來這個步驟非常重要,由於Nextjs是SSR所以接收訊息的Function必須放在
useEffect
內,打開app/page.tsx
並修改
tsx1... 2const firebaseConfig = {...}; //你的firebaseConfig 3const app = initializeApp(firebaseConfig); 4 5const requestForToken: any = async (messaging: Messaging) => { 6 return getToken(messaging, { 7 vapidKey: "", 8 }) 9 .then((currentToken) => { 10 if (currentToken) return currentToken; 11 12 return null; 13 }) 14 .catch((err) => { 15 return null; 16 }); 17}; 18 19useEffect(() => { 20 const messaging = getMessaging(app); 21 22 onMessage(messaging, (payload) => { 23 new Notification(payload.notification.title, { 24 body: payload.notification.body, 25 icon: payload.notification.icon, 26 }); 27 }); 28}, []); 29...
- 這裡解釋一下上面的參數和函式
requestForToken: 用來獲取用戶token的函式,這樣Firebase才知道要推播給誰。
vapidKey: 這是一組公鑰,可以前往 專案設定 -> 雲端通訊 -> Generate key pair
,來獲得。
onMessage: 將拿到的消息透過訊息框的方式通知使用者。
發送訊息
接著讓我們來發送訊息測試一下是否成功:
- 新增一個nodejs專案,不會的可以前往這裡看步驟1。
npm i firebase-admin
- 修改
index.js
javascript1const admin = require("firebase-admin"); 2 3admin.initializeApp({ 4 credential: admin.credential.cert("./key.json"), 5}); 6 7const tokens = [...]; 8function test() { 9 // 建立訊息內容 10 tokens.map((token) => { 11 const message = { 12 notification: { 13 title: "測試訊息標題", 14 body: "我是內容", 15 }, 16 token: token, 17 }; 18 19 // 推播訊息 20 admin 21 .messaging() 22 .send(message) 23 .then((response) => { 24 console.log("Successfully sent message:", response); 25 }) 26 .catch((error) => { 27 console.log("Error sending message:", error); 28 }); 29 }); 30} 31test();
keyjson: 可以從firebase 專案設定 -> 服務帳戶 -> 產生私密金鑰
來獲得。
tokens: 放入用 requestForToken
此函式獲取的token。
- 執行
node index.js
,這樣你的網頁就能獲得推播的訊息了。
在沒有開啟網頁的情況下接收推播
這就是我們使用Service Worker的目的了:
- 在電腦端的使用者 "點選網址最右邊的安裝"。
- 在手機端的使用者將該網頁 "加入主畫面"。
- 接著關閉網頁。
- 在執行一次
node index.js
來看看效果。
最後
這樣今天的目標就完成了,此網頁左上方的鈴鐺,就是本篇的功能展示,各位可以按下訂閱來獲取我最新的消息,下次見~