HTMX代表未来
Web 应用程序开发的现状
如今,用户对 Web 的期望是拥有这种超级流畅的无需重新加载的体验。然而,这种期望通常由依赖于 React 和 Angular 等库和框架的单页应用 (SPA) 来实现,而这些库和框架非常专业,使用起来可能非常复杂。
一种新方法是将这种用户体验 (UX) 的能力交还给在 SPA 热潮之前构建网站的工程师,利用他们现有的工具集和知识,而 HTMX 是我迄今为止使用过的最好的例子。
SPA的成本
SPA 允许工程师创建一些出色的 Web 应用程序,但它们是有代价的:
-
架构和开发者经验的复杂性都大幅增加。你必须花费大量时间学习框架。
- 就构建和打包代码而言,工具是一个不断变化的领域。
- 管理客户端和服务器上的状态
- 框架,在库之上,在其他库之上,在 polyfill 之上。React甚至建议在其技术之上使用框架:
React 是一个库。它允许你组合组件,但并未规定如何进行路由和数据获取。要使用 React 构建整个应用,我们推荐使用全栈 React 框架。
-
胖客户端本质上要求客户端执行大量的 JavaScript。如果您拥有现代硬件,这没问题,但对于使用较旧硬件或网络连接缓慢且不稳定的用户来说,这些应用程序将无法使用且运行缓慢。
- 错误地制作 SPA 非常容易,您需要使用带有钩子的正确方法来避免最终导致糟糕的客户端性能。
-
一些 SPA 实现会舍弃渐进式增强(一个值得注意且高尚的例外是Remix)。因此,大多数 SPA必须启用 JavaScript。
-
如果您希望使用 JavaScript 或 TypeScript 以外的其他语言,则必须走过危险的转译之路。
-
它在许多公司中造成了后端和前端的孤岛,带来了高昂的协调成本。
在 SPA 出现之前,您需要选择自己喜欢的语言,并将 HTML 传递给用户的浏览器以响应 HTTP 请求。这种做法虽然不错,但交互性很差,而且在某些情况下,可能会让 UI 变得难以使用,尤其是在每次交互时页面都需要完全重新加载的情况下。为了解决这个问题,您通常会添加不同数量的 JS 代码来提升用户体验。
虽然有些人觉得这种方法有些过时,但它启发了REST的原始论文,尤其是关于超媒体的部分。构建网站的超媒体方法使得万维网取得了令人难以置信的成功。
超媒体?
以下是来自数据 API 而非超媒体的响应。
{
"sort": "12-34-56",
"number": "87654321",
"balance": "123.45"
}
为了使这些数据在 SPA 中有用,代码必须理解结构并决定呈现什么以及提供哪些控件。
REST 描述了超媒体的使用。超媒体是指您的响应不仅仅是原始数据,而是一个描述媒体的有效负载(例如 HTML 标签,例如<p>
、 标头等)以及如何操作媒体(例如form
、input
)。
服务器返回描述银行账户的 HTML,并附带某种形式的控件来处理资源,这就是超媒体的一个例子。现在,服务器负责决定如何渲染数据(CSS 略有不同)以及应该显示哪些控件。
<dl>
<dt>Sort</dt><dd>12-34-56</dd>
<dt>Number</dt><dd>87654321</dd>
<dt>Balance</dt><dd>£123.45</dd>
</dl>
<form method="POST" action="/transfer-funds">
<label>Amount <input type="text" /></label>
<!-- etc -->
<input type="submit" value="Do transfer" />
</form>
这种方法意味着您有一个通用客户端,即 Web 浏览器;它了解如何显示超媒体响应并允许用户使用“控件”来做任何他们需要的事情。
……当浏览器刚出现的时候,一个通用的网络客户端能够通过这种疯狂的超媒体技术与任何应用程序通信的想法真的非常新颖。现在依然如此。
如果你在 1980 年告诉某人,“你知道吗 - 你将使用相同的软件来访问你的新闻、你的银行、你的日历、这个叫做电子邮件的东西以及所有这些东西”,他们一定会斜眼看着你,他们不知道你在说什么,除非他们碰巧是研究这类东西的小型研究小组之一。
虽然表面上,构建 SPA 的人们谈论使用“RESTful” API 为其客户端代码提供数据交换,但这种方法并不是纯粹意义上的 RESTful,因为它不使用超媒体。
许多开发人员没有使用通用客户端,而是创建了定制客户端,这些客户端必须理解从 Web 服务器获取的原始数据,然后根据这些数据渲染控件。通过这种方式,浏览器更像是 JavaScript、HTML 和 CSS 的运行时。
顾名思义,更胖的客户端比瘦客户端需要投入更多的精力和成本。然而,“原始”的超媒体方法显然不足以满足当今的所有需求;浏览器支持的控件以及使用这些控件需要刷新整个页面的方式意味着,对于我们需要开发的许多类型的 Web 应用来说,用户体验不够好。
HTMX 和超媒体
与 SPA 不同,HTMX不会抛弃 REST 的架构方法;它增强了浏览器,提高了其超媒体功能,并且使得提供丰富的客户端体验变得更加简单,而无需编写太多 JavaScript(如果有的话)。
您可以像以前一样,使用任何您喜欢的编程语言来交付 HTML。这意味着您可以使用久经考验的成熟工具,采用“真正的 RESTful”方法,从而实现更加直接的开发方式,并减少意外的复杂性。
HTMX 允许您设计从服务器获取HTML 片段的页面,以根据需要更新用户页面,而无需烦人的整页加载刷新。
我们现在将通过经典的 TODO 列表应用程序在实践中看到这一点。
Clojure HTMX TODO
首先,请不要过分担心这是用 Clojure 编写的。我用 Clojure 写这个只是为了好玩,但这种方法的妙处在于,你可以使用任何你喜欢的语言,只要它能响应 HTTP 请求即可。
这里没什么特别的,但感觉确实像个 SPA。没有整页重新加载;它非常流畅,就像你见过的所有其他 SPA 演示一样。
这里的区别是:
- 我没有编写任何 JavaScript。
- 我也没有通过将 Clojure 转换为 JavaScript 来作弊。(请参阅ClojureScript)
我制作了一个使用超媒体响应 HTTP 请求的 Web 服务器。
HTMX 增加了定义更丰富的超媒体的能力,让您可以注释任何 HTML 元素,以要求浏览器发出 HTTP 请求来获取 HTML 片段并将其放在页面上。
编辑控件
这个演示中最令人兴奋和印象深刻的部分是编辑操作。输入框会立即出现供您编辑,然后又会快速更新,这种操作感觉像是需要大量原生 JS 代码或类似 React 的方式才能实现,但你会发现它非常简单。
我们先来看一下 TODO 项的标记。为了清晰起见,我删掉了非编辑标记。
<li hx-target="closest li">
<form action="/todos/2a5e549c-c07e-4ed5-b7d4-731318987e05" method="GET">
<button hx-get="/todos/2a5e549c-c07e-4ed5-b7d4-731318987e05" hx-swap="outerHTML">📝</button>
</form>
</li>
它可能看起来很多,但要理解编辑功能的工作原理,主要需要关注以下几点:
- 在 上
<li>
,一个属性hx-target
会告诉浏览器:“当你获取要渲染的片段时,这就是我希望你替换的元素”。子元素会继承此属性,因此对于此 内的任何 HTMX 操作<li>
,HTML
返回的 都会替换 的内容<li>
。 hx-get
编辑按钮意味着当您单击它时,HTMX
会告诉浏览器HTTP GET
对执行URL
并获取一些新的标记来代替<li>
现有的内容。- 该表单对于示例来说并不是必需的,但它允许我们为非 JavaScript 用户支持该功能,这将在后面介绍。
当您开始使用 HTMX 时,了解正在发生的事情的一个简单方法是查看浏览器的开发人员工具中的网络。
当用户点击编辑按钮时,浏览器会对特定的待办事项资源执行HTTP GET
。服务器返回一个超媒体响应,该响应是该资源带有一些超媒体控件的表示。
<form action="/todos/45850279-bf54-4e2e-a95c-c8c25866a744/edit"
hx-patch="/todos/45850279-bf54-4e2e-a95c-c8c25866a744" hx-swap="outerHTML" method="POST">
<input name="done" type="hidden" value="false"/>
<input name="name" type="text" value="Learn Rust"/>
<input type="submit"/>
</form>
然后,HTMX 会获取该 HTML 并替换我们定义的hx-target
。因此,用户现在看到的是这些用于操作资源的超媒体控件,而不是之前图片中显示的行。
你会注意到表单有一个hx-patch
属性,这意味着当表单提交时,浏览器会发送一个PATCH
包含数据的 来更新资源。然后服务器会响应更新后的内容并进行渲染。
拥抱网络
HTMX 还有更多内容,但这是方法的关键,这与 SPA 流行之前大多数网站所采用的方法相同。
- 用户前往
URL
- 服务器返回超媒体(HTML),即带有控件的内容。
- 浏览器渲染超媒体
- 用户可以使用这些控件来执行工作,从而导致从浏览器向服务器发送 HTTP 请求。
- 服务器执行业务逻辑,然后返回新的超媒体供用户使用
HTMX 所做的就是通过为我们提供更多关于触发 HTTP 请求的选项,并允许我们更新页面的一部分而不是重新加载整个页面,从而使浏览器在超媒体方面表现更好。
通过拥抱超媒体,而不是将浏览器仅仅视为 JavaScript 运行时,我们获得了很多简单的好处:
- 我们可以使用任何编程语言。
- 我们不需要大量的库和其他杂物来维护 Web 开发的基本优势。
- 缓存
- SEO友好性
- 返回按钮按预期工作
- ETC。
- 为不愿意或不能使用 JavaScript 的用户提供支持非常容易
最后这一点对我和我目前的雇主来说至关重要。我所在的公司致力于开发全球使用的产品,我们的内容和工具必须尽可能地方便更多人使用。我们不能容忍因为技术选择不当而将某些人排除在外。
这就是我们采用渐进增强方法的原因。
渐进式增强是一种设计理念,它为尽可能多的用户提供基本内容和功能的基础,同时仅为能够运行所有必需代码的最现代浏览器的用户提供最佳体验。
TODO 应用中的所有功能(搜索、添加、编辑、删除、标记为完成)在 JavaScript 关闭的情况下均可使用。HTMX 并非“免费”实现这些功能,它仍然需要工程方面的投入,但由于其方法本身,实现起来更简单。我大约花了一个小时的时间,并且没有进行重大改动。
它如何支持非 JavaScript
当浏览器发送由 HTMX 提示的请求时,它会添加一个 header HX-Request: true
,这意味着在服务器上,我们可以相应地发送不同的响应,非常类似于内容协商。
处理程序的经验法则大致是:
parseAndValidateRequest()
myBusinessLogic()
if request is htmx then
return hypermedia fragment
else
return a full page
end
下面是处理新 TODO 的 HTTP 处理程序的具体示例:
(defn handle-new-todo [get-todos, add-todo]
(fn [req] (let [new-todo (-> req :params :todo-name)]
(add-todo new-todo)
(htmx-or-vanilla req
(view/todos-fragment (get-todos))
(redirect "/todos")))))
第三行是我们的“业务逻辑”,调用一个函数将新的 TODO 添加到我们的列表中。
第四行是一些代码,用于确定我们正在处理什么类型的请求,后续行要么渲染一个片段以返回,要么重定向到页面。
到目前为止,这似乎是我使用 HTMX 开发超媒体应用时反复出现的主题。从架构本身来看,如果支持页面部分更新,则返回一个片段;否则,浏览器需要重新加载整个页面,因此要么重定向,要么直接返回整个 HTML。
服务器端 HTML 模板技术已经非常成熟。市面上有很多选项和优秀的指南,教你如何构建和添加自动化测试。更重要的是,它们都提供了一些组合功能,因此返回片段或整个页面的操作非常简单。
为什么说是未来?
显然,我无法预测未来,但我相信 HTMX(或类似的东西)将在未来几年成为制作 Web 应用程序越来越流行的方法。
最近,HTMX 被宣布为 GitHub Accelerator 中的 20 个项目之一
它使“前端”更易于访问。
学习 React 本身就是一个行业。它发展迅速,变化多端,有海量的内容需要学习。我非常理解那些曾经开发过成熟应用的开发者,他们被现代前端开发所困扰,宁愿被归类为“后端”开发者。
我用 React 做过一些相当复杂的系统,虽然有些系统挺有意思的,但要达到效果,你需要学习的内容量对于大多数应用来说太过庞大。React 有它的优势,但对于很多 Web 应用来说,它有些矫枉过正了。
使用 HTMX 的超媒体方法并不难掌握,尤其是如果你具备一些 REST 基础知识(很多“后端”开发人员都应该具备)。它为更广泛的人群打开了创建丰富网站的机会,让他们不必学习如何使用框架,也不必跟上不断变化的形势。
减少客户流失
即使 React 已经存在十多年了,它仍然感觉不够稳定和成熟。几年前,Hooks 还是个新奇的东西,每个人都必须学习并用它来重写所有组件。在过去的六个月里,我的 Twitter 上充斥着关于这个新奇的“RSC”(React 服务器组件)的讨论和教程。Joy 表情。
使用 HTMX 让我能够充分利用 15-20 年前学到的、至今仍在使用的东西,比如我的网站。这种方法易于理解且有据可查,而且最佳实践独立于编程语言和框架。
我用 Go和Clojure编写了示例应用,一点问题都没有,而且我完全不懂 Clojure。一旦你掌握了一门语言的基本语法,并学会了如何响应 HTTP 请求,你就有足够的能力开始学习了;而且你可以复用架构和设计的最佳实践,而不必一遍又一遍地学习新的方法。
如果你必须使用 Angular,那么你有多少 React 技能可以迁移到 Angular?从一个 React 框架切换到另一个 React 框架容易吗?当 Class 组件变得“糟糕”,大家都希望你使用 Hooks 时,你是什么感受?
更便宜
只需付出更少的努力!
Hotwire是一个与 HTMX 目标相似的库,由 Ruby on Rails 驱动。DHH 发布了以下推文。
这就是为什么听到“全栈”这个词被贬义,或者被当成一个不可能完成的任务,会让人如此沮丧。我们必须成为一个由前端、后端、服务或其他任何专家组成的分散团队才能做很酷的事情。绝对不是。
无需理解 SPA 世界中庞大的框架以及制作胖客户端的固有复杂性,您就可以用更少的工程师创建丰富的 Web 应用程序。
更具弹性
如前所述,使用超媒体方法,制作无需 JavaScript 即可运行的 Web 应用程序相对简单。
同样需要记住的是,浏览器是一个不可信的环境,因此在构建 SPA 时,必须格外谨慎。你必须在客户端实现大量的业务逻辑;但由于架构原因,这些逻辑也需要在服务器上进行复制。
例如,假设我们想要一条规则,规定如果待办事项标记为已完成,则无法编辑它。在单页应用 (SPA) 中,我会获取原始 JSON,并且必须使用业务逻辑来确定是否在客户端代码的某个位置渲染编辑按钮。但是,如果我们想确保用户无法规避这一点,我必须在服务器上实施同样的保护措施。这听起来风险低且简单,但这种复杂性会累积起来,并且错位的可能性也会增加。
采用超媒体方法时,浏览器是“愚蠢的”,无需担心这一点。作为开发人员,我可以在服务器端捕获此规则。
降低协调复杂性
SPA 的复杂性导致了后端和前端孤岛的转变,这需要付出代价。
典型的后端/前端团队划分会导致团队合作效率低下,例如交接和沟通不畅,并使工作更加难以完成。许多人误以为个人效率是最重要的指标,并以此为借口建立这些团队。他们看到大量的 PR 被合并,产生了大量的问题,却忽略了协调成本。
例如,假设你想在页面中添加一条新数据或添加一个新按钮。对于许多团队来说,这需要团队之间召开会议,讨论并商定新的 API,创建供前端团队使用的模拟代码,并最终协调发布。
在超媒体方法中,你完全没有这种复杂性。如果你想在页面上添加一个按钮,你可以直接添加,无需协调工作。你不必太担心 API 设计。你可以随意更改标记和内容。
团队通过 JSON 交换数据,如果不谨慎处理,可能会非常脆弱,而且总是需要协调成本。像消费者驱动契约这样的工具可以提供帮助,但这只是另一种工具,另一种需要理解的东西,也是一种容易出错的地方。
这并不是说没有专业化的空间。我曾经工作过一些团队,工程师们“端到端”地构建 Web 应用程序,但我们也有一些精通语义和易访问标记的专家,他们帮助我们确保工作质量。无需协商 API 并将工作交给其他人来构建网站,这真是令人无比自由。
更多选项
在服务器端渲染 HTML 是一条非常常见的途径。目前,市面上已经存在许多久经考验的成熟工具和库,可用于在各种主流编程语言以及大多数小众编程语言中从服务器生成 HTML。
总结
我鼓励那些希望降低 Web 应用开发成本和复杂性的开发者尝试一下 HTMX。如果您因为前端开发难度过高而犹豫不决,那么 HTMX 或许是一个不错的选择。
我并不是说 SPA 现在是多余的;当您需要非常复杂和快速的交互时,仍然需要它们,而往返服务器获取一些标记是不够的。
2018 年,我曾断言,相当多的 Web 应用程序可以用比 SPA 简单得多的技术方法编写。如今,随着 HTMX 之类的技术的出现,这一论断更有说服力。前端领域目前的主要趋势是等待新的框架来解决你之前使用的框架的问题。SPA 方法本质上比超媒体方法更复杂,堆积更多的技术可能并非解决之道,不妨尝试一下超媒体。
请查看下面的一些链接以了解更多信息。
进一步阅读和聆听
- HTMX 的作者写了一本优秀的免费书籍,讲解了超媒体。这本书通俗易懂,会颠覆你对构建 Web 应用的认知。如果你只创建过 SPA,那么这本书绝对值得一读。
- HTMX。特别是示例部分,很好地展示了各种可能性。文章也很棒。
- 我很幸运被邀请参加GoTime 播客,与 HTMX 的创始人 Carson Gross 一起讨论它!虽然这是一个 Go 播客,但大部分讨论都围绕着超媒体方法展开。
- Go 版本是我第一次使用 HTMX,创建了本文中描述的待办事项列表应用程序
- 我和同事 Nicky 一起开发了Clojure 版本
- Hotwire 上的 DHH
- 渐进增强
- 五年前,我写了《我想要的 Web》,在文中我抱怨 SPA 成本的飙升。最初,我是因为看到伴侣那台用了两年的 ChromeBook 在一个热门网站上卡住了,而这个网站本来可以用静态 HTML 来运行的。在文章中,我谈到了我希望 Web 能够更多地坚持基本的超媒体方法,在服务器上渲染 HTML,并使用渐进式增强来提升体验。现在回过头来看这篇文章,我非常欣慰 HTMX 之类的技术已经到来了。