3.4 实时数据与事件监听


3.4 实时数据与事件监听

数字/事实:传统做法让前端每 3 秒轮询一次接口查新数据,一千个在线用户就是每秒三百次无效请求,数据库白白承压。Supabase Realtime 改成"数据变了才推",把无效流量压到接近零。这一节我们讲它怎么工作、怎么用,以及 broadcast 和 presence 这两个常被忽略的能力。

从一次卡顿反推:为什么别轮询

Realtime 服务订阅 Postgres 的 WAL(预写日志)。当你 insert 一行,PG 先把这行写进 WAL,Realtime 读到后通过 WebSocket 推给订阅的客户端。所以延迟取决于 WAL 到 WebSocket 的管道,而不是前端多久问一次。

前提:要监听的表需在 Dashboard 的 Realtime 设置里开启,或建表时加 replica identity。下面代码开启对某表的实时支持(SQL 侧):

-- 让表支持行级实时(记录旧值用于更新/删除推送) alter publication supabase_realtime add table public.todos;

postgres_changes:监听表变更

最常用的是监听某张表的增删改:

const channel = supabase .channel('todos-room') .on( 'postgres_changes', { event: '*', schema: 'public', table: 'todos' }, (payload) => { // payload.eventType: INSERT / UPDATE / DELETE // payload.new / payload.old 是新旧行 console.log('变更:', payload.eventType, payload.new) } ) .subscribe() // 控制台会打印类似:变更: INSERT { id: 5, content: '新任务', done: false }

event: '*' 监听全部,也可只听 'INSERT'。还能加 filter 只听某些行(如 filter: 'user_id=eq.' + myId),减少无用推送。

broadcast 与 presence:不只是数据库变更

很多新手以为 Realtime 只能听数据库。其实它还有两个不依赖表的通道:

  • broadcast:客户端之间互发自定义事件,适合光标位置、打字状态、信令。
  • presence:跟踪"谁在线",适合显示当前房间人数。
const room = supabase.channel('collab-room', { config: { presence: { key: myUserId } }, }) room .on('broadcast', { event: 'cursor' }, (e) => { drawCursor(e.payload.x, e.payload.y) }) .on('presence', { event: 'sync' }, () => { const online = room.presenceState() console.log('在线人数:', Object.keys(online).length) }) .subscribe() // 自己移动光标时广播 room.send({ type: 'broadcast', event: 'cursor', payload: { x: 99, y: 40 } })

下面 SVG 把三种实时能力的关系画清,避免混淆"听库"和"听人":

03-04-fig01

展开案例:多人协作文档的光标同步

背景:做一个简单在线白板,多个用户能看到彼此光标实时移动。

操作过程:

  1. 开启频道并配置 presence 标识自己:
const board = supabase.channel('whiteboard', { config: { presence: { key: me }, broadcast: { self: false } }, }) board .on('broadcast', { event: 'move' }, (e) => renderCursor(e.payload)) .on('presence', { event: 'sync' }, () => showOnline(room.presenceState())) .subscribe()
  1. 鼠标移动时发 broadcast(节流到每 50ms 一次):
canvas.addEventListener('mousemove', (ev) => { board.send({ type: 'broadcast', event: 'move', payload: { x: ev.offsetX, y: ev.offsetY, who: me } }) })
  1. 别人移动时,自己的 broadcast 监听渲染对方光标。

结果:无需任何后端代码,光标在所有人屏幕上实时同步;presence 自动处理某人关闭页面后的"下线"。

解读:这个案例完全没碰数据库——光标是瞬态信号,不该落库(否则白板一卡就写爆表)。broadcast 正适合这种"高频、可丢、不持久"的数据。若把光标写进表再用 postgres_changes 听,既慢又费存储,是典型的错配。

变式:若白板内容(图形)要持久化,那图形变更走 postgres_changes(落 shapes 表),光标走 broadcast(瞬态),两通道各司其职。

你现在已经能分清三个通道

  • Realtime 基于 WAL 推送,替代轮询,大幅降无效请求。
  • 三通道:postgres_changes 听库、broadcast 听人、presence 看在线。
  • 瞬态信号用 broadcast,持久数据用 postgres_changes,别混。

提醒:监听表变更同样受 RLS 约束。如果你没给某表开对应 select 策略,客户端收不到它的变更推送——不是 Realtime 坏了,是权限挡了。

建议:我们建议给 broadcast 做节流(如 50ms)并设 self: false 避免收到自己发的回声。高频事件不节流会把 WebSocket 打满,反而让实时变卡。

下一节讲文件存储:头像、图片、文档怎么上传、权限怎么管、大文件怎么处理。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U