Supabase 实时数据库订阅

FreeGuideOnline 最新 2026-07-11

sql ALTER TABLE your_table_name REPLICA IDENTITY FULL;


> 注意:`REPLICA IDENTITY FULL` 会为每条更新在 WAL 日志中写入完整的旧行数据,可能会稍微增加 WAL 日志体积,请根据业务需求权衡。

### 2. 将表加入实时发布(Publication)

实时功能通过 PostgreSQL 的发布(Publication)`supabase_realtime` 来决定哪些表会生成变更事件。默认情况下,所有表都不会自动加入。你需要手动添加目标表:

```sql
ALTER PUBLICATION supabase_realtime ADD TABLE your_table_name;

如果你有很多表,也可以一次添加多张:

ALTER PUBLICATION supabase_realtime ADD TABLE table1, table2, table3;

或者允许未来新建的表自动加入(通过事件触发器,高级用法可参考官方文档)。

提示:每次变更结构后无需重复执行。只要表曾被加入该发布,后续的结构变更仍会生效。

在客户端订阅变更

Supabase 提供两种风格的实时订阅方式:

  1. 通道订阅(推荐,channel() API):使用 supabase.channel() 创建通道并监听 PostgreSQL 更改,灵活性更高,支持多种过滤器和自定义逻辑。
  2. 简易订阅(旧版 from().on().subscribe()):直接在查询构建器上调用,语法简洁,适合单表简单监听。

我们以更现代的通道订阅为主进行讲解。

初始化 Supabase 客户端

无论在什么环境下,首先需要初始化客户端。

import { createClient } from '@supabase/supabase-js'

const supabase = createClient(
  'https://your-project.supabase.co',
  'your-anon-key'  // 或 service_role key,视安全需求而定
)

创建通道并订阅表变更

下面的示例演示如何订阅 messages 表的所有变更。

const channel = supabase
  .channel('db-changes')            // 通道名称,可自定义,同一客户端内不可重复
  .on(
    'postgres_changes',
    {
      event: '*',                   // 监听所有事件 (INSERT, UPDATE, DELETE)
      schema: 'public',
      table: 'messages'
    },
    (payload) => {
      console.log('变更事件:', payload.eventType);
      console.log('新数据:', payload.new);
      console.log('旧数据:', payload.old);
    }
  )
  .subscribe((status) => {
    if (status === 'SUBSCRIBED') {
      console.log('已成功订阅 messages 表');
    }
  })
  • event:可指定 'INSERT''UPDATE''DELETE''*'
  • payload.new:包含变更后的行数据(INSERT 和 UPDATE 事件)。DELETE 事件中此字段为 null
  • payload.old:包含变更前的行数据(UPDATE 和 DELETE 事件),INSERT 事件中为 null
  • payload.eventType:字符串,明确本次事件类型。

仅监听特定行的变化(带过滤条件)

大多数场景下你不需要监听整张表,而是只关心某些特定条件的数据变化。可以在 on() 的选项中添加过滤器。

Supabase 的实时过滤器支持大部分 PostgREST 操作符,如 eqneqgtltin 等。

示例:只订阅某位用户的消息

supabase
  .channel('user-messages')
  .on(
    'postgres_changes',
    {
      event: 'INSERT',
      schema: 'public',
      table: 'messages',
      filter: `user_id=eq.${userId}`  // 字符串格式,类似 PostgREST 查询参数
    },
    (payload) => {
      console.log('收到属于自己的新消息:', payload.new)
    }
  )
  .subscribe()

你也可以组合多个过滤器:

filter: `user_id=eq.${userId}&room=eq.${roomId}`

多条件 OR 过滤器

如果你需要一次订阅满足多个条件(逻辑或)的变化,可以创建多个 on() 监听器,或者使用 in 操作符。

// 监听 status 为 'urgent' 或 'new' 的行
filter: `status=in.(urgent,new)`

取消订阅与资源清理

当组件卸载或不再需要监听时,必须取消订阅以释放连接。

// 取消整个通道及其所有监听
supabase.removeChannel(channel)

若想只移除通道内的某个监听,可以在创建时保存监听器引用,然后调用 .unsubscribe() —— 不过实践中直接移除整个通道更常见。

在前端框架中的集成实战(以 React 为例)

我们将通过一个简单的聊天消息实时展示,演示如何订阅新消息并在组件卸载时自动关闭订阅。

import { useEffect, useState } from 'react'
import { supabase } from './supabaseClient'

export default function ChatRoom({ roomId }) {
  const [messages, setMessages] = useState([])

  useEffect(() => {
    // 获取历史消息(一次性)
    const fetchMessages = async () => {
      const { data } = await supabase
        .from('messages')
        .select('*')
        .eq('room', roomId)
        .order('created_at', { ascending: true })
      setMessages(data || [])
    }

    fetchMessages()

    // 订阅该房间的新 INSERT 消息
    const channel = supabase
      .channel(`room-${roomId}`)
      .on(
        'postgres_changes',
        {
          event: 'INSERT',
          schema: 'public',
          table: 'messages',
          filter: `room=eq.${roomId}`
        },
        (payload) => {
          // 将新消息追加到状态末尾
          setMessages((prev) => [...prev, payload.new])
        }
      )
      .subscribe()

    // 组件卸载时清理
    return () => {
      supabase.removeChannel(channel)
    }
  }, [roomId])

  return (
    <div>
      {messages.map((msg) => (
        <div key={msg.id}>{msg.text}</div>
      ))}
    </div>
  )
}

后端监听场景(Node.js / Deno)

你同样可以在服务端利用实时订阅构建事件驱动架构,例如通知其他服务、更新缓存等。

// Node.js 服务端代码
const { createClient } = require('@supabase/supabase-js')

const supabase = createClient(SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY)

const channel = supabase
  .channel('order-updates')
  .on(
    'postgres_changes',
    { event: 'INSERT', schema: 'public', table: 'orders' },
    (payload) => {
      // 执行业务逻辑:发送邮件、更新库存等
      console.log('新订单:', payload.new)
    }
  )
  .subscribe()

⚠️ 在服务端请使用 service_role 密钥,绕过 RLS(行级安全)策略;注意密钥安全,不要暴露给客户端。

更新和删除事件的高级处理

实时数据同步经常需要处理数据修改和移除的场景。比如一个协作白板,需要根据 UPDATE 事件更新坐标,根据 DELETE 事件移除形状。

.on('postgres_changes', { event: 'UPDATE', schema: 'public', table: 'shapes' }, (payload) => {
  const updatedShape = payload.new;
  // 在本地画布中根据 id 更新形状
})

.on('postgres_changes', { event: 'DELETE', schema: 'public', table: 'shapes' }, (payload) => {
  const removedId = payload.old.id;
  // 从画布中移除对应形状
})