Rails Turbo Stream Broadcast - 被低估的 Rails 功能

Rei · August 11, 2025 · Last by lyfi2003 replied at August 12, 2025 · 173 hits

原文链接:https://geeknote.net/Rei/posts/3253


我一直认为,把 Rails 作为前后端分离项目的后端是一种浪费。类似于在 Mac 上安装 Windows 系统,没有什么可以阻止你这样做,而且看起来很美好:Windows 系统更大众化,Mac 硬件做工好,似乎是强强联合。但是深度 Mac 用户会知道,这丢失了很多软硬件一体化设计的优势。

Rails 优势在于前后端紧密集成,有的功能只有在全栈环境下才能使用,Turbo Stream Broadcast 就是其中一个。Broadcast 在官方文档中只有一个小节的介绍,并且没有完整示例,可能很多人会忽略。我在掌握这个功能之后,发现 broadcast 对于开发实时更新的应用提供了很大便利。

最近我开发了一个 AI Chat 应用,其中核心功能是聊天部分:

这个功能是通过 broadcast 实现的。当用户发送一条消息时,服务端会保存这条消息,同时生成一个和 AI 交互的后台任务,在任务执行过程中又会创建新的消息。消息的创建和更新都通过 broadcast 更新到页面上。

关键部分的代码如下:

# app/models/message.rb
class Message < ApplicationRecord
  belongs_to :conversation

  broadcasts_to :conversation
end
<!-- app/views/rooms/show.html.erb -->
<div id="messages">
  ...
</div>

<%= turbo_stream_from @conversation %>
<!-- app/views/messages/_message.html.erb -->
<div id="<%= dom_id(message) %>">
  ...
</div>

有了这些代码,message 对象就会在创建、更新和删除的时候通过 action cable 发送消息,在页面更新内容。

其中最浓缩的代码是这一行:

broadcasts_to :conversation

初见可能比较难理解,将它展开成完整参数的形式:

broadcasts_to(
  :conversation,
  inserts_by: :append,
  updates_by: :replace,
  target: "messages",
  partial: "messages/message",
  locals: ->(message) { { message: message } }
)

这些参数的意思是:

  • 将 message 的变动发布到关联的 conversation 频道。
  • 渲染 message 的时候使用 messages/message 模版。
  • 新增时插入到 #messages 元素。
  • 更新时替换 dom_id(message) 元素。
  • 删除时删除 dom_id(message) 元素。

Broadcast 默认提供 CRUD 的 turbo stream action,前面例子的消息内容流式更新使用了自定义的 turbo stream action。为避免文章篇幅过长不在这里展开,有兴趣的可以看项目源码:https://github.com/chloerei/llmrpg

broadcasts_to 就像 belongs_to,不了解细节的时候会觉得它充满魔法,了解细节之后会觉得原来如此,然后自觉使用默认配置。

对应的,前端部分需要插入 <%= turbo_stream_from @conversation %> 来订阅 @conversation 频道的消息,这样它就能在后台数据变动的时候更新页面内容。

Broadcast 功能需要视图、控制器、模型、后台任务等模块高度集成,才能实现这么精炼的代码。试想一下如果没有这个功能,自己就需要解决 websocket 服务端/客户端开发、通信协议,还有两端开发带来的沟通问题。而使用 Rails 全栈,只需要 broadcasts_to

如果你需要为应用增加一些实时功能,推荐使用 turbo stream broadcast。你也许会对 Rails 高集成带来的开发效率有全新的认识。

Rails 在快速推出产品能力方面是无敌的

You need to Sign in before reply, if you don't have an account, please Sign up first.