You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Rails与Hotwire Turbo Streaming流式加载长页面?

实现Rails Turbo流式逐条渲染消息

要实现页面加载时每隔100ms显示一条消息,核心是让Rails分块流式输出Turbo Stream内容,而非一次性返回所有渲染结果。以下是两种可行方案:

方案一:整页流式渲染(页面加载时直接输出流)

这种方式在HTML响应中直接流式输出Turbo Stream标签,页面加载过程中浏览器会逐步接收并渲染消息。

1. 修改控制器

启用流式响应,确保Rails分块发送内容:

def index
  @messages = Message.last(100)
  # 设置响应类型并开启流式渲染
  response.headers['Content-Type'] = 'text/html; charset=utf-8'
  render stream: true
end

2. 修改索引页面模板(index.html.erb)

循环渲染每条消息的Turbo Stream局部视图,每条后添加延迟并立即刷新响应流:

<div id="messages"></div>

<% @messages.each do |message| %>
  <%= render partial: 'message', locals: { message: message } %>
  <% sleep 0.1 %> <!-- 模拟每条渲染耗时100ms -->
  <% response.stream.flush %> <!-- 立即发送当前块内容到浏览器 -->
<% end %>

<% response.stream.close %>

方案二:页面加载后发起流式Turbo请求

先加载空页面,再通过JS发起流式Turbo请求,后台分块返回消息的Turbo Stream内容。

1. 新增控制器动作(或复用现有动作)

专门处理流式Turbo响应:

def stream_messages
  @messages = Message.last(100)
  # 设置Turbo Stream响应类型并开启流式渲染
  response.headers['Content-Type'] = 'text/vnd.turbo-stream.html; charset=utf-8'
  render stream: true
end

2. 创建Turbo Stream模板(stream_messages.turbo_stream.erb)

分块输出每条消息的Turbo Stream:

<% @messages.each do |message| %>
  <%= turbo_stream.append :messages, partial: 'message', locals: { message: message } %>
  <% sleep 0.1 %>
  <% response.stream.flush %>
<% end %>
<% response.stream.close %>

3. 修改索引页面模板(index.html.erb)

添加JS在页面加载后发起流式请求,并让Turbo处理返回的流内容:

<div id="messages"></div>

<script>
  document.addEventListener('turbo:load', () => {
    fetch('/messages/stream', { // 对应stream_messages的路由
      headers: { 'Accept': 'text/vnd.turbo-stream.html' }
    })
    .then(response => {
      const reader = response.body.getReader();
      const decoder = new TextDecoder('utf-8');

      const processChunk = (result) => {
        if (result.done) return;
        // 解码并让Turbo处理当前块的Turbo Stream内容
        Turbo.renderStreamMessage(decoder.decode(result.value, { stream: true }));
        return reader.read().then(processChunk);
      };

      return reader.read().then(processChunk);
    });
  });
</script>

关键说明

  • 你现有代码的问题:turbo_stream.append配合collection会一次性生成所有Turbo Stream标签并返回,浏览器会一次性渲染所有消息,无法实现逐条显示。必须通过response.stream.flush分块发送内容。
  • 同步sleep仅用于简化示例,生产环境建议用异步处理(如Active Job)避免阻塞服务器线程。
  • 确保服务器支持流式响应(Puma默认支持,Unicorn需额外配置)。

内容的提问来源于stack exchange,提问作者B Seven

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 19:12:40