原文链接: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 } }
)
这些参数的意思是:
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 高集成带来的开发效率有全新的认识。