第二章:Supabase Realtime 實戰:監聽資料庫變化並讓前端 UI 自動即時更新
Supabase 被譽為 Firebase 的最強開源替代品,而它最殺手級的功能之一,就是 Supabase Realtime。
傳統開發中,如果你想做一個「只要有人新增資料,畫面上立刻多一筆紀錄」的功能,你需要自己架設 Socket.io 伺服器、處理連線斷線重連、還要擔心多個節點間的狀態同步。現在,Supabase 幫你把這一切都封裝成了幾行簡單的 JavaScript 程式碼。
步驟一:在 Supabase 後台開啟 Realtime 權限
基於效能與資安考量,Supabase 預設是關閉 Realtime 推播的。你必須明確告訴它「哪些資料表需要被廣播」。
- 登入 Supabase 專案儀表板。
- 前往左側選單的 Database > Replication。
- 找到 Source,點擊 `0 tables` (或目前的數字) 以開啟設定。
- 找到你要監聽的表,例如我們建立一個用來儲存聊天訊息的表 `chat_messages`。
- 把它旁邊的開關打開 (Toggle ON)。
步驟二:前端建立監聽頻道 (Channel)
回到我們的 Next.js 前端專案。 假設你已經有了一個聊天室元件 `ChatRoom.tsx`,裡面有一個 `messages` 的 useState 來儲存目前畫面上的訊息。
我們要加上一段 `useEffect`,用來向 Supabase 註冊一條「專屬廣播頻道」。
```typescript "use client" import { useEffect, useState } from "react" import { createClient } from "@/utils/supabase/client"
export default function ChatRoom() { const [messages, setMessages] = useState<any[]>([]) const supabase = createClient()
useEffect(() => { // 1. 定義頻道的名稱 (可以自訂,例如 'public-chat') const channel = supabase.channel('public-chat')
// 2. 訂閱 Postgres 的變更事件 (INSERT)
channel.on(
'postgres_changes',
{
event: 'INSERT', // 我們只想監聽「新增」事件
schema: 'public', // 預設 schema
table: 'chat_messages' // 指定我們要監聽的資料表
},
(payload) => {
// 3. 當有新資料塞進資料庫時,這個 Callback 就會被觸發!
console.log("收到新訊息囉!", payload.new)
// 把新收到的訊息,加入到 React 的 State 中,觸發畫面重新渲染
// 注意:永遠使用函數式的 setMessages 寫法來避免閉包陷阱
setMessages((prevMessages) => [...prevMessages, payload.new])
}
)
// 4. 正式發起訂閱連線
.subscribe((status) => {
console.log("連線狀態:", status);
})
// 5. Cleanup 函式:當使用者離開此頁面時,記得退訂頻道以節省資源
return () => {
supabase.removeChannel(channel)
}
}, [])
// 渲染聊天畫面 return (
步驟三:測試即時魔法
這段程式碼的魔法在哪裡? 你可以打開兩個不同的瀏覽器視窗(例如一個 Chrome,一個 Safari),同時進入這個聊天室頁面。
然後,你可以直接打開 Supabase 後台的 Table Editor,手動在 `chat_messages` 資料表裡面插入一筆新資料。 砰! 不到 0.1 秒的時間,你打開的兩個瀏覽器視窗上,會「同時且瞬間」浮現出你剛才輸入的新訊息,完全不需要按重新整理 (F5)!
這背後完全沒有寫任何的 `fetch` 輪詢,也沒有寫龐大的 Socket.io Node.js 伺服器,一切都發生在雲端與瀏覽器之間的 WebSocket 管線中。
深入解析:它是怎麼辦到的?
當你在前端執行 `.subscribe()` 時,Supabase JS SDK 默默地在背景與 Supabase 伺服器建立了一條 WebSocket 連線。
Supabase 伺服器內部有一個超強的 Elixir 開源模組,它會去監聽 PostgreSQL 資料庫底層的「WAL (Write-Ahead Log,寫入預先日誌)」。只要資料庫一發生變動,它就立刻把變動轉成 JSON 格式,順著 WebSocket 管線噴給所有正在訂閱這個 Table 的前端客戶端。
常見陷阱:為什麼我收不到推播?
如果你照著寫卻收不到更新,請檢查以下三個最常踩坑的地方:
- 忘記在後台開啟 Replication(佔 90% 的新手錯誤)。這就好像你買了收音機,但電台沒有發送訊號一樣。
- RLS (Row Level Security) 擋住了:如果你開啟了 RLS,但沒有寫對應的 Policy 允許匿名 (anon) 或登入者讀取,推播會被攔截。
- State 更新邏輯寫錯:在 `setMessages` 裡面,如果你寫的是 `setMessages([...messages, payload.new])`,會產生 Closure Trap(閉包陷阱),導致每次都只留下一筆資料。永遠記得使用函數式的寫法 `setMessages(prev => [...prev, new])`。
學會這招,你已經具備了開發股票看盤、多人共編協作工具的基礎了!下一章,我們要把難度升級,結合 AI API 來實作炫酷的打字機串流特效。