【译】Hotwire 和 HTMX - 同一原则,不同方法
这是 Radan Skorić日前发布的 Hotwire and HTMX - Same Principles, Different Approaches的一篇译文。
Hotwire和HTMX是两个有着相同目标的强大工具库:简化现代 Web 应用程序的构建。
两者都拥抱 Web 的 HTML+CSS 基础,并在此基础上增强,以实现无需或少需 JavaScript 的流畅用户交互。
虽然这两个项目有许多共同的基本价值观甚至目标,但它们采取不同的方法来实现这些目标。读完本文后,您将对它们的异同有一个很好的高层次理解。
共同的基本原则:HTML 的力量
这两个框架都认识到以 HTML 为中心的方法的价值。这与在浏览器内实现胖客户端模式的众多单页应用程序 (SPA) JavaScript 框架形成鲜明对比。它们将 HTML 视为一种渲染媒介。
另一方面,像 Hotwire 和 HTMX 这样的框架认识到,当将 HTML 视为应用程序状态引擎时,它会更强大。 HTML 作为瘦客户端技术的设计原则虽然很古老,但仍然有效。 HTML 已经取得了很大的进步,现代浏览器中的 HTML5 + CSS 组合非常强大。然而,构建丰富的现代交互式应用程序还不够。差不多了,但还没有完全实现。
框架如 Hotwire 和 HTMX旨在缩小差距并使服务器端渲染 HTML 能够提供现代用户体验。它们是用 javascript 实现的,但它们被设计为需要尽可能少的自定义应用程序 javascript。事实证明,这使得大多数现代 Web 应用程序的构建变得更加容易。
对框架的简短介绍
本文不是这两个框架的教程。我将尝试为您提供这两个框架的最简短介绍,仅足够让那些对这两个框架不熟悉或两者都不熟悉的人理解文章的其余部分。
Hotwire
_如果您对 Hotwire 有哪怕是最基础的了解,我建议您直接跳到下一章。
Hotwire代表 HTML Over the Wire 。它是多个库的总称。主要的就是Turbo 。
Turbo 通过引入一些关键概念来增强 HTML:
- Turbo Drive 拦截常规浏览器导航,转而执行 AJAX 请求并自行处理响应。它通过避免整个页面重新加载来加快导航速度。它还添加了缺失的 HTML 功能,例如使链接执行非 GET 请求的功能。
- Turbo Frames 将页面分解为独立的上下文。它们在页面中实现页面的心理模型。这提供了一种自然的方式来构建位于同一页面上的集成 UI,但实现起来就像是一组单独的页面一样简单。例如:内联编辑可以被视为包含在框架中的单个页面上的多页面编辑流程。
- Turbo Streams 允许服务器呈现修改页面特定部分的小型目标指令。它们作为对表单提交的响应或通过 Websockets 提供,以启用协作应用程序。
所有 Turbo 功能的核心在于,开发过程应尽可能地像构建一个普通的、服务器端渲染的 HTML 应用程序一样,只是它表现得更好。所有的业务逻辑都在服务器端,而界面则通过向客户端发送 HTML 来控制。
如果这还不够,Hotwire 的第二个库就可以发挥作用: Stimulus 。您可以将 Stimulus 视为 jQuery 的现代替代品:
- 简化 DOM 操作,使其轻松响应事件并修改。
- 不接管 HTML,而是将 HTML 视为应用程序状态的来源。
- 最好用于添加 HTML 缺乏的开箱即用的特定功能。
它通过附加小型_Stimulus 控制器_来实现此目的,这些控制器附加到特定 DOM 元素,并允许您使用 javascript 通过某些自定义行为来增强该元素。例如,添加 data-action="mouseenter->popup#show"
当我们将鼠标悬停在元素上时,使用popup
控制器将导致控制器上的show
javascript 方法运行。
HTMX
_如果您对 HTMX 有哪怕是最基础的了解,我建议您直接跳到下一章。
HTMX 代表超文本标记扩展 。它旨在通过允许以下功能来增强 HTML:
- 任何元素都可以发出 HTTP 请求。
- 任何事件都可以触发 HTTP 请求。
- 请求可以使用任何 HTTP 动词。
- 页面的任何部分都可以使用 HTTP 响应进行更新。
它通过一组属性来实现这一点,这些属性可以放置在任何元素上,并通过 HTMX 实现的额外功能来增强元素。这样的属性还有很多,这里仅举几个例子,让大家感受一下: hx-post="url"
会将 HTTP 请求从常规 GET 转换为 POST。所有 HTTP 动词都存在等效属性。hx-swap="outerHTML"
将使 HTMX 使用 HTTP 响应替换整个目标元素,而不仅仅是其内容。- 当我们将鼠标移到元素上时,
hx-trigger="mouseenter"
将触发 HTTP 请求,而不是等待单击。 hx-target="#container"
将使用 HTTP 响应来更新具有 idcontainer
元素,而不是我们单击的元素。
还有许多其他属性。其中一些支持迷你 DSL,以进一步定制行为。所有属性都是正交且互补的。总体思路是结合这些支持 HTMX 的属性来构建复杂的界面。
例如,这是一个列表元素,当按住 ctrl 键单击时,将向\generate
发出发布请求,并将响应添加为列表中的最后一项:
<ul hx-post="\generate" hx-trigger="click[ctrlKey]" hx-swap="beforeend">
<li> Click while holding Ctrl to generate new items </li>
</ul>
相似之处
从根本上来说,两者都基于同一个前提:
- HTML 和 CSS 可以提供丰富的交互体验界面。
- 构建仅使用 HTML 进行渲染的胖客户端的单页应用程序方法会增加附带的复杂性。
- 通过将 HTML 视为应用程序状态的来源并将其呈现在服务器上,可以避免额外的复杂性。
综合考虑,他们得出的结论,采用不同的方法可以极大地简化 Web 应用程序的开发。
两者都采用相同的高级方法:
- 将业务逻辑保留在服务器上。
- 让 HTML 成为应用程序状态的存储。
- 增强 HTML 以实现现代用户体验。
- 仅当增强的 HTML 不够时才使用 Javascript 进行自定义行为。
从远处观察,这两个解决方案看起来非常相似,就好像它们几乎是同一件事。
仔细观察,他们采用不同的原则,导致他们的感受存在显着差异。剧透一下:我认为两者都很好,各有各的优点。
差异之处
他们的设计选择不同之处在于:
- 显式与隐式增强的比率。
- 达到何种程度后,您将需要编写自定义 Javascript。
- 它需要多少后端才能充分利用其功能。
隐式与显式增强
增强 HTML 的框架需要做出选择:何时应由应用程序代码显式激活增强功能,何时应根据上下文隐式激活。换句话说:我们希望这个框架有多“魔法”的感觉?
Hotwire,特别是 Turbo,非常倾向于“魔法”:
- 默认情况下,一旦您在页面上导入 Turbo,它就会激活 Turbo Drive 并接管导航。您可以将其关闭,但默认情况下它会启用一系列增强功能,而无需您执行任何操作。
- Turbo Frames 努力让“页中页”的心智模型发挥作用。对于最常见的情况,如果将框架放置在正确的位置,所有常见路径都将正常工作。框架默认启用许多增强功能。您应该使用自定义属性主要是为了打破默认行为。
这是故意的。如果您熟悉 Ruby on Rails,您会注意到 Hotwire 从 Rails 继承了这种方法。
HTMX 采用了完全不同的方法。它明确选择成为一个仅显式执行操作的低级增强库。当页面中包含 HTMX 时,默认情况下它会执行: Nothing 。所有功能都是通过向元素显式添加hx-
属性来启用的。没有属性,没有增强。
唯一的例外是hx-boost
,当将其放置在元素上时,它将增强该元素中包含的链接和表单。将hx-boost
放在 BODY 标签上可以让您获得 Turbo Drive 开箱即用的一些功能。然而,HTMX 强调这是对 仅具有显式增强功能 的库哲学的务实例外。
两种方法都有其优点和缺点: - Turbo 将需要您在大多数功能上投入更少的工作,但当“魔法”失效时,可能会遇到更多问题。
- HTMX 需要更多工作,但会让您准确控制它的哪些部分在特定元素上处于活动状态。
没有 javascript 你能走多远
如果你想在尽可能不编写自定义 JavaScript 的情况下做尽可能多的事情,HTMX 会带你走得更远。由于它被设计为一个底层工具包,它拥有大量可以以多种方式组合的属性,从而实现许多不同的行为。你可能需要一段时间才能学会所有属性及其选项,但这样会让你受益匪浅。它的领域特定语言(DSL)甚至允许通过直接在 hx-
属性中定义的 CSS 过渡来实现动画。
另一方面,Turbo 在幕后非常努力地为你提供一个连贯的增强功能心智模型,这样你就可以在不记住太多选项的情况下走得很远。Turbo 的增强功能全部集中在与服务器的交互上。一旦你需要额外的 UI 行为,它就会引导你转向 Stimulus,并期望你在其中编写一小段自定义 JavaScript 代码。
同样,这两种方法都有其优点和缺点:
- HTMX 的 API 接口面更广,需要记忆的内容更多,但默认提供的功能也更丰富。
- Turbo 和 Stimulus API 让记住它们提供的所有功能变得更容易,但你将更频繁地需要使用自定义 JavaScript。
两者都没有错,这在很大程度上取决于您和您的团队感觉更自然的方式。
它需要从后端获得多少
Hotwire 源自一个后端框架:Ruby on Rails。 HTMX 没有。这一点显而易见。 Hotwire 仍然是后端无关,但是,如果您使用 Rails,它会提供出色的集成,并处理许多事情。并且,它建议其他框架使用 Rails 集成作为 参考后端集成。 HTMX 对后端的关注要少得多。
这表明功能集何时确实需要特定的后端部分才能发挥作用。例如,Hotwire 的协作功能,只与 Rails 配合使用。通过 websocket 与后端集成是 Turbo 的一流功能。 Stimulus 经过微调,可以很好地处理从后端传输的更改。一旦您拥有正确的后端集成,使用 Hotwire 获得协作功能就变得非常简单。 HTMX 确实支持 websockets 集成,但它是一个扩展,书中甚至没有提到。
Hotwire 和 HTMX 都可以与任何后端堆栈一起使用,这就是 HTML 在线/超媒体方法的全部要点。但是,根据您选择的后端技术栈以及应用程序的要求,您可能会发现其中一个更容易集成。
不出所料,对于 Ruby on Rails 来说, turbo-rails gem比htmx-rails gem更加成熟。
结束语
非常有趣的是,从相同的基本价值观出发,竟然可以得出两个不同但都高质量且尊重基本原理的解决方案,同时使用感受却截然不同。
作为一名长期的 Rails 开发人员,我选择的库是 Hotwire。当它按预期工作时我喜欢它,但有时我希望当我需要做不同的事情时它更明确。阅读hx-
属性的许多选项让我思考如何构建我的 Stimulus 控制器以采取微型的 DSL,从而允许直接从 HTML 进行更多控制。
那么总体来说哪一个更好呢?就像编程中的许多问题一样,答案令人沮丧:这取决于情况。
您呢?您当前的团队和项目更喜欢哪种方法?