上一篇文章以白话文的形式简单讲了一下 Turbo Frames,这篇文章打算介绍 Turbo 的最后一个部件 Turbo Streams。同样我会结合实际场景提供代码示例。原文链接 https://www.lanzhiheng.com/posts/turbo-streams-simple-talking
上一篇文章讲过 Turbo Frames 的作用跟 iframe 很像,设定一个区域,区域中的任何操作都只会影响该区域的内容。在不需要编写 JavaScript 的前提下这种做法还是挺方便的,不过也有它的局限性每次请求的操作有限,只能影响区域内的内容。如果想要页面其他区间作出响应,依旧需要额外编写 JavaScript 代码。.
Turbo Streams进一步增强了我们对页面的操控能力。
Turbo Streams deliver page changes as fragments of HTML wrapped in self-executing elements. Each stream element specifies an action together with a target ID to declare what should happen to the HTML inside it.
依然是不需要写 JavaScript 代码,后端服务以声明的方式提供 HTML 模板,以 DOM 的 id 作为唯一标识,并配上对应的动作,Turbo Streams 接收到这个模板之后就会
模板大概长这个样子
<turbo-stream action="append" target="messages">
<template>
<div id="message_1">
This div will be appended to the element with the DOM ID "messages".
</div>
</template>
</turbo-stream>
篇幅有限我就不全部贴了,官方文档都有。这里表示它会寻找 id 为messages的容器,然后往里面追加template
标签中的内容。类似的动作还有prepend
, replace
, update
, remove
,意思也就是字面上的意思。
也就是说,它只提供了较为基本的 5 个操作,足以满足日常工作中的大部分 DOM 操作需求。如果需要更加复杂的交互,再考虑使用 JavaScript 来专门解决。
先把 WebSocket 或者 SSE 放一边,看看普通的请求模式 Turbo Streams 的能力。下面是我最近对博客系统管理后台的重写,需求很简单
效果如下:
虽然对一个自己用的系统来说,这种做法稍微有点过度优化了,直接跳页面岂不是简单很多?不过只要依赖于 Turbo Streams 做成这种交互并不会比重新刷页面要复杂多少,姑且一试。
Controller 部分很简单,只需要提供turbo_stream
的对应响应即可。
module Musk
class PostsController < ApplicationController
def toggle
original = resource.draft
resource.update(draft: !original)
message = original ? '上架成功' : '下架成功'
flash.now[:notice] = "《#{resource.title}》#{message}"
respond_to do |format|
format.turbo_stream { render :toggle, locals: { post: resource } } # turbo_stream响应
format.html { redirect_back(fallback_location: collection_path) }
end
end
end
end
这里要提供两种渠道,一种是响应 HTML 的,另一种是响应 Turbo Streams,这个后台会根据请求头的Accept
字段来判断。
接下来只需要像传统的 Rails 项目那样提供对应的模板就行,只不过这次的模板的后缀是turbo_stream.erb
<!-- app/views/musk/posts/toggle.turbo_stream.erb -->
<!-- 修改对应资源在表格中的状态,假设资源的id为1,那么就修改id="table_online_1"的单元格 -->
<turbo-stream action="update" target=<%= "table_online_#{post.id}" %>>
<template>
<%= boolean_tag(post.online) %>
</template>
</turbo-stream>
<!-- 修改触发按钮的内容,“上架”按钮,请求结束后会变成“下架”按钮,反之亦然 -->
<turbo-stream action="replace" target=<%= "toggle_link_#{post.id}" %>>
<template>
<%= toggle_link(post) %>
</template>
</turbo-stream>
<!-- 显示flash信息,注意我这里用的是flash.now,因为只需要对当前渲染生效,页面跳转后消失 -->
<turbo-stream action="update" target="alert-area">
<template>
<%= alert_area %>
</template>
</turbo-stream>
简单到有点难以置信吧?篇幅有限,boolean_tag
, toggle_link
, alert_area
这些帮助方法我就不贴了,大家可以根据自己的需要去定制。如果需要参考相关源码可以到笔者的Stone仓库查看。
可见只需要少量的代码,配合服务端渲染,不需要写任何 JavaScript 代码,就能同时操作页面的不同区域,只不过要事先给待调整的地方做上id
唯一标记。这对于熟悉 Rails,并不想写太多 JavaScript 的人来说确实是个不错的工具。我们能够很容易就写出交互性较好的页面。
这篇文章通过案例简单讲解了一下 Turbo Streams 的基本用法,由于笔者还没有 Websocket 或者 SSE 相关的使用场景,这个放在以后再说。作为对 Turbo Frames 的补充,Turbo Streams 只提供了 5 种(append, prepend, replace, update, remove)基本操作。不过对于大多数场景来说已然足够,或许也是符合 80/20 的原则?然而作为一个依赖服务端渲染的工具集 Turbo Streams 依然有它的局限性,毕竟还是有一些场景 5 种基本操作无法满足的,这个时候我们就需要依赖 JavaScript 去解决了,接下来的Stimulus框架是 Rails 社区的首选。