訪問人數:

Firebase - 讓React(web)也可以接收訊息推播

2024-10-17 03:00

前言

這幾天和朋友聊到手機App的推播,讓我聯想到Web端是否也可以像App一樣有接收推播的功能,當下越想越興奮,於是就誕生出了這篇文章。

今日目標

在部落格上實作訂閱功能

要達成這個功能除了使用 Firebase Cloud Messaging 外,還需要再專案內加入PWA和註冊Service Worker,原因之後會提到,PWA的介紹和教學在這裡

P.S 由於這篇文章程式碼比較多,各位請慢慢看才不會漏掉小細節倒致無法成功。

註冊Firebase

  1. 前往Firebase點擊右上方的 Go to console,註冊並登入。
  2. 點選建立專案,輸入專案名稱後,點選建立。
  3. 進入 專案設定 -> 一般設定 -> 點選 </> 的圖標 來建立網頁應用。
  4. 完成後,會得到 firebaseConfig 這樣就完成註冊了。

在專案內加入PWA

由於我的Blog是使用Nextjs,PWA的配置方法會略有不同:

  1. npm i next-pwa
  2. 修改 next.config.mjs
javascript
1import 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;
  1. public下新增 manifest.json 和一張 192x192的圖片
json
1{ 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}
  1. 修改 app/layout.tsx
tsx
1... 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

  1. public下新增 sw.js
javascript
1self.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});
  1. 修改 app/page.tsx
tsx
1... 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...
  1. 確認是否完成PWAService Worker: 查看網址最右邊是否有出現 安裝 且打開 F12 -> Application 查看Service Workers是否有註冊成功。

讓Web可以接收FireBase訊息

接下來我們要讓專案可以接收到推播的訊息:

  1. npm i firebase
  2. public下新增 firebase-messaging-sw.js 並將剛註冊的 firebaseConfig 貼上
javascript
1importScripts( 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});
  1. 接下來這個步驟非常重要,由於Nextjs是SSR所以接收訊息的Function必須放在 useEffect 內,打開 app/page.tsx 並修改
tsx
1... 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...
  1. 這裡解釋一下上面的參數和函式

requestForToken: 用來獲取用戶token的函式,這樣Firebase才知道要推播給誰。

vapidKey: 這是一組公鑰,可以前往 專案設定 -> 雲端通訊 -> Generate key pair,來獲得。

onMessage: 將拿到的消息透過訊息框的方式通知使用者。

發送訊息

接著讓我們來發送訊息測試一下是否成功:

  1. 新增一個nodejs專案,不會的可以前往這裡看步驟1。
  2. npm i firebase-admin
  3. 修改 index.js
javascript
1const 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。

  1. 執行 node index.js,這樣你的網頁就能獲得推播的訊息了。

在沒有開啟網頁的情況下接收推播

這就是我們使用Service Worker的目的了:

  1. 在電腦端的使用者 "點選網址最右邊的安裝"。
  2. 在手機端的使用者將該網頁 "加入主畫面"。
  3. 接著關閉網頁。
  4. 在執行一次 node index.js 來看看效果。

最後

這樣今天的目標就完成了,此網頁左上方的鈴鐺,就是本篇的功能展示,各位可以按下訂閱來獲取我最新的消息,下次見~