简介
Turbo 捆绑了多种技术,用于创建快速、现代、渐进增强的 Web 应用程序,而无需使用大量 JavaScript。它提供了一种更简单的替代方案,以取代流行的客户端框架,这些框架将所有逻辑放在前端,并将应用程序的服务器端限制为仅比 JSON API 稍多一点的内容。
使用 Turbo,你可以让服务器直接传递 HTML,这意味着用于检查权限、直接与域模型交互以及编程应用程序所需的一切逻辑或多或少都可以完全在您最喜欢的编程语言中实现。你不再需要在 JSON 分隔的两侧镜像逻辑。所有逻辑都存在于服务器上,而浏览器只处理最终的 HTML。
您可以在 Hotwire 网站 上阅读更多有关这种 HTML over the wire 方法的好处。以下是 Turbo 为实现这一目标而带来的技术。
﹟ Turbo Drive:在持久进程中导航
与旧式的独立页面方法相比,传统单页面应用程序的一个主要吸引力在于导航速度。SPA 的很多速度来自不持续终止应用程序进程,而只在下一页重新初始化它。
Turbo Drive 通过使用相同的持久进程模型为你提供相同的速度,但不要求你围绕范例构建整个应用程序。没有需要维护的客户端路由器,也没有需要小心管理的状态。持久进程由 Turbo 管理,你可以像在早期阶段一样编写服务器端代码——完全不受当今 SPA 庞然大物的复杂性的影响!
这通过拦截所有点击同一域名的 <a href>
链接来实现。当您点击一个符合条件的链接时,Turbo Drive 会阻止浏览器对其进行跟踪,使用 History API 更改浏览器的 URL,使用 fetch
请求新页面,然后呈现 HTML 响应。
表单也是如此。它们的提交被转换为 fetch
请求,Turbo Drive 将从中跟踪重定向并呈现 HTML 响应。
在呈现期间,Turbo Drive 将替换 <body>
元素的内容并合并 <head>
元素的内容。JavaScript 窗口和文档对象以及 <html>
元素从一个呈现持续到下一个呈现。
虽然可以直接与 Turbo Drive 交互以控制访问发生的方式或挂接到请求的生命周期,但大多数情况下,这是一种直接替换,只需采用一些约定即可免费获得速度。
﹟ Turbo Frames:分解复杂页面
大多数 Web 应用程序显示包含多个独立段落的页面。对于讨论页面,您可能在顶部有一个导航栏,在中间有一个消息列表,在底部有一个用于添加新消息的表单,以及一个带有相关主题的侧边栏。生成此讨论页面通常意味着以序列化方式生成每个段落,将它们全部拼凑在一起,然后将结果作为单个 HTML 响应传递给浏览器。
使用 Turbo Frames,您可以将那些独立的段落放置在可以限定其导航并可以延迟加载的框架元素中。限定范围的导航意味着框架内所有交互(例如点击链接或提交表单)都发生在该框架内,从而使页面的其余部分不会发生更改或重新加载。
要将独立的段落包装在其自己的导航上下文中,请将其包含在 <turbo-frame>
标记中。例如
<turbo-frame id="new_message">
<form action="/messages" method="post">
...
</form>
</turbo-frame>
当您提交上面的表单时,Turbo 会从重定向的 HTML 响应中提取匹配的 <turbo-frame id="new_message">
元素,并将其内容交换到现有的 new_message
框架元素中。页面的其余部分保持原样。
除了限定范围的导航之外,框架还可以延迟加载其内容。要延迟加载框架,请添加一个 src
属性,其值为将自动加载的 URL。与限定范围的导航一样,Turbo 会从结果响应中找到并提取匹配的框架,并将其内容交换到适当的位置
<turbo-frame id="messages" src="/messages">
<p>This message will be replaced by the response from /messages.</p>
</turbo-frame>
这听起来可能很像老式的框架,甚至像 <iframe>
,但 Turbo Frames 是同一 DOM 的一部分,因此没有与“真实”框架相关的任何怪异或妥协。Turbo Frames 由相同的 CSS 设置样式,是同一 JavaScript 上下文的一部分,并且不受任何其他内容安全限制。
除了将你的片段变成独立的上下文之外,Turbo Frames 还为你提供了
- 高效缓存。在上面的讨论页面示例中,每当出现一个新的相关主题时,相关主题侧边栏需要过期,但中心的消息列表不需要过期。当所有内容都只是单一页面时,只要任何单个片段过期,整个缓存就会过期。使用框架时,每个片段都会独立缓存,因此你可以获得具有更少依赖项密钥的更长寿命的缓存。
- 并行执行。每个延迟加载的框架都由其自己的 HTTP 请求/响应生成,这意味着它可以由一个单独的进程处理。这允许并行执行,而无需手动管理进程。一个复杂的复合页面从头到尾完成需要 400 毫秒,可以使用框架将其分解,其中初始请求可能仅需要 50 毫秒,并且三个延迟加载的框架中的每一个都需要 50 毫秒。现在,整个页面在 100 毫秒内完成,因为三个框架中的每一个都同时运行 50 毫秒,而不是按顺序运行。
- 适用于移动设备。在移动应用程序中,你通常无法拥有大型、复杂的复合页面。每个片段都需要一个专门的屏幕。使用 Turbo Frames 构建的应用程序,你已经完成了将复合页面转换为片段的工作。然后,这些片段可以出现在本机工作表和屏幕中,而无需更改(因为它们都具有独立的 URL)。
﹟ Turbo Streams:提供实时页面更改
响应异步操作进行部分页面更改是我们让应用程序感觉活跃的方式。虽然 Turbo Frames 在单个框架内的直接交互中为我们提供了此类更新,但 Turbo Streams 允许我们响应通过 WebSocket 连接、SSE 或其他传输发送的更新来更改页面的任何部分。(想象一个 imbox,当新邮件到达时会自动更新。)
Turbo Streams 引入了 <turbo-stream>
元素,其中包含八个基本操作:append
、prepend
、replace
、update
、remove
、before
、after
和 refresh
。使用这些操作以及指定你想要操作的元素的 ID 的 target
属性,你可以对刷新页面所需的全部突变进行编码。你甚至可以在单个流消息中组合多个流元素。只需将你感兴趣的 HTML 包含在 template 标记 中插入或替换,Turbo 会完成剩下的工作
<turbo-stream action="append" target="messages">
<template>
<div id="message_1">My new message!</div>
</template>
</turbo-stream>
此流元素将使用新消息获取 div
,并将其追加到 ID 为 messages
的容器中。替换现有元素同样简单
<turbo-stream action="replace" target="message_1">
<template>
<div id="message_1">This changes the existing message!</div>
</template>
</turbo-stream>
这是 Rails 世界中最初称为 RJS,然后称为 SJR 的概念延续,但无需任何 JavaScript 即可实现。好处保持不变
- 重用服务器端模板:实时页面更改使用与创建首次加载页面相同的服务器端模板生成。
- 通过网络传输 HTML:由于我们发送的只是 HTML,因此您不需要任何客户端 JavaScript(当然除了 Turbo)来处理更新。是的,HTML 有效负载可能比同类 JSON 大一点,但使用 gzip 后,差异通常可以忽略不计,并且您可以节省客户端获取 JSON 并将其转换为 HTML 所需的所有工作。
- 更简单的控制流:当消息到达 WebSocket、SSE 或响应表单提交时,可以非常清楚地跟踪发生的情况。无需路由、事件冒泡或其他间接操作。它只是要更改的 HTML,包装在一个告诉我们如何更改的单个标记中。
现在,与 RJS 和 SJR 不同,不可能在 Turbo Streams 操作中调用自定义 JavaScript 函数。但这是一种特性,而不是一个缺陷。当响应中发送了太多 JavaScript 时,这些技术很容易最终产生混乱。Turbo 专注于仅更新 DOM,然后假设您将使用 Stimulus 操作和生命周期回调连接任何其他行为。
﹟ Turbo Native:适用于 iOS 和 Android 的混合应用程序
Turbo Native 是为 iOS 和 Android 构建混合应用程序的理想选择。您可以使用现有的服务器渲染 HTML 在原生包装器中获得应用程序功能的基本覆盖范围。然后,您可以花所有节省下来的时间来制作真正受益于高保真原生控件的几个屏幕,使其变得更好。
像 Basecamp 这样的应用程序有数百个屏幕。重新编写所有这些屏幕将是一项巨大的任务,但收益却很小。最好将原生火力保留给真正需要最高保真度的触控交互。例如,Basecamp 中的“为您提供”收件箱,我们在其中使用需要感觉恰到好处的滑动控件。但大多数页面,比如显示单个消息的页面,如果完全采用原生方式,也不会真正变得更好。
采用混合模式不仅可以加快开发进程,还能让你更自由地升级应用,而无需经历缓慢且繁琐的应用商店发布流程。在 HTML 中完成的任何操作都可以在 Web 应用程序中进行更改,并立即对所有用户生效。无需等待大型科技公司批准你的更改,也无需等待用户升级。
Turbo Native 假设你正在使用适用于 iOS 和 Android 的推荐开发实践。这不是一个抽象原生 API 甚至尝试让你的原生代码可以在平台之间共享的框架。可共享的部分是服务器端渲染的 HTML。但原生控件是用推荐的原生 API 编写的。
请参阅 Turbo Native: iOS 和 Turbo Native: Android 存储库以获取更多文档。请参阅 iOS 和 Android 上的 HEY 原生应用,了解 Turbo 驱动的混合应用可以有多好。
﹟ 与后端框架集成
你无需任何后端框架即可使用 Turbo。所有功能都构建为直接使用,无需进一步抽象。但是,如果你有机会使用与 Turbo 集成的后端框架,你会发现生活简单得多。 我们已经为 Ruby on Rails 创建了此类集成的参考实现。