大多数人只用过Supabase的数据库变更推送,却不知道这套实时系统还有两块拼图——能让你的应用像Figma那样多人协作,像Discord那样显示在线状态,而且不需要写一行后端代码。

三件套全貌:数据库只是三分之一

打开网易新闻 查看精彩图片

Supabase Realtime把实时能力拆成了三个独立模块。数据库变更(DB Changes)监听表的增删改,这是文档里写得最多的。广播(Broadcast)让客户端之间直接发消息,不碰数据库。在线状态(Presence)追踪谁正在使用你的应用。

这三个功能共享同一套通道(Channel)机制,但解决的是完全不同的问题。搞混它们的适用场景,轻则浪费资源,重则数据丢失。

广播的核心卖点是"无服务器"——消息从A客户端发出,经Supabase中转,直达B客户端。整个过程不写入数据库,延迟压到最低,但也不会留下任何记录。

代码层面,你先创建一个命名通道,然后监听特定事件类型。比如多人协作场景里,每个光标移动都是一个广播事件:

接收端用onBroadcast注册回调,从payload里解析用户ID和坐标,更新本地状态。发送端调用sendBroadcastMessage,把当前鼠标位置打包发出去。Supabase官方文档里的示例就是这套光标同步逻辑。

这种模式最适合三类场景:实时共享光标位置、游戏状态同步、协作白板的手绘笔迹。还有直播投票的中间结果——观众能看到票数在跳动,但刷新页面就清零,因为数据从没存过。

在线状态:谁正在看这份文档

Presence功能解决的是"谁在线"这个经典难题。传统做法是用心跳包定期更新数据库,但并发一高就崩。Supabase的做法是把状态托管给Realtime服务,开发者只负责声明"我在线"以及携带什么信息。

实现步骤很固定:创建通道、订阅、调用track()上报自己的状态。状态内容完全自定义——用户ID、昵称、头像URL、当前状态、最后活跃时间,想带什么带什么。

同步回调onPresenceSync会在任意用户状态变化时触发。你从presenceState()拿到全量在线列表,展开、映射、更新UI。还有更细粒度的onPresenceJoinonPresenceLeave,适合用来做"某某加入了房间"的提示动画。

文档里的示例代码直接展示了头像堆叠效果:取前5个在线用户的头像,超出的人数用"+N"显示。这种设计在Notion、Figma里随处可见,现在几行Flutter代码就能复刻。

关键细节:Presence状态是"尽力同步"的。用户断网或杀进程,服务端会在超时后自动清理。但你不能依赖它做精确的权限控制——如果有人绕过客户端直接调API,Presence状态反映不了。

组合拳:什么时候该混用

真正复杂的应用需要同时启用多个功能。文档给了一个典型模式:同一个通道既监听数据库变更,又处理广播消息,还维护在线列表。

规则写得很清楚——重要数据走数据库变更,确保持久化和可查询;临时数据走广播,追求最低延迟。光标位置每秒更新几十次,写数据库是自杀行为;但文档标题的修改必须落库,否则刷新页面就丢了。

代码结构上,Flutter的级联调用语法让配置变得可读:先onPostgresChanges挂文档表的监听,再onBroadcast挂光标事件,最后onPresenceSync处理在线用户。一次subscribe()启动所有订阅。

这里有个容易踩的坑:三个回调共享同一个通道,但它们的错误处理是独立的。数据库变更可能因权限失败,广播可能因频率限制被丢弃,Presence可能因网络抖动短暂失联。生产环境需要分别捕获异常,而不是一个try-catch包到底。

通道生命周期:别在内存里养僵尸

文档的最后一部分讲通道管理,但示例代码被截断了。从已有内容能推断出核心模式:把RealtimeChannel实例存成状态变量,在initState里初始化,在页面销毁时手动关闭。

Flutter的StatefulWidget很适合做这个——_channel字段持有连接,_setupChannel异步初始化。如果用户快速切换页面,旧通道必须及时释放,否则内存泄漏加上无意义的网络开销。

Supabase Realtime的通道是有状态的。订阅成功后,服务端会维护这个连接的元数据,包括你注册的回调、当前的Presence状态、订阅的过滤器条件。客户端断连后重连,这些状态需要重新同步,中间可能有短暂的数据窗口期。

文档没提但值得关注的:通道命名是全局的。'room:$roomId'这种格式意味着所有客户端按相同规则命名就能进同一个房间。如果roomId是用户输入的,需要校验格式防止注入——虽然Realtime不像SQL那样危险,但奇怪的字符可能导致匹配失败。

三个功能的性能特征差异很大。广播是纯粹的内存操作,吞吐量最高;Presence需要服务端维护状态表,单通道人数过多会触发限制;数据库变更依赖Postgres的复制槽,表级监听在大表高并发时有延迟。

选型建议:光标追踪用广播,在线列表用Presence,文档内容同步用数据库变更。如果三者都要,像文档示例那样混用,但做好心理准备——调试时会面对三个不同的失败模式。

Supabase把这三件套打包成Realtime品牌,但底层是三个独立系统。理解它们的边界,比记住API参数更重要。毕竟,用广播存持久数据,或者用数据库变更传光标位置,都能跑通,都是灾难。