对 htmx 最常见的批评之一,通常来自第一次听说它的人,是这样的:
你在抱怨现代前端框架的复杂性,但你的解决方案只是另一个复杂的前端框架。
这是一个绝佳的反对意见!对于你引入项目的任何第三方(3P)代码,这都是一个正确的问题。即使你没有自己编写 3P 代码,通过将其包含在项目中,你就承诺要去理解它——并且如果想升级它,还需要更新这种理解。这是一个重大的承诺。
让我们将这个批评拆解成几个组成部分,并准确评估 htmx 在多大程度上陷入了它声称要解决的弊端中。
一些 htmx 的辩护者急忙帮腔道:"htmx 不是框架,它是一个库。"这可能是错误的。
"框架"是一个口语化的术语——没有硬性规定来界定第三方代码何时从"库"演变为"框架"——但我们仍需尝试定义它。在此语境下:
如果你喜欢比喻:库是你添加到机器中的一个齿轮,框架是预构建的机器,你通过定制其齿轮来控制它。
这个区别虽然模糊,但很重要,因为它描述了第三方代码的替换难易程度。例如,使用 CSV 解析库的 JavaScript 服务可能可以轻松换用不同的 CSV 解析库;而使用 NextJS 框架的 JavaScript 服务很可能在其整个生命周期内都依赖 NextJS,因为大部分代码都基于与 NextJS 结构交互的假设编写。
因此,如果你的服务构建在框架之上,其有效寿命就与该框架的有效寿命绑定。如果该框架被废弃、遭人厌恶或变得不可用,修改项目的难度将稳步增加,直到你放弃修改,最终彻底废弃它。
当人们问"htmx 只是另一个 JavaScript 框架吗?"时,他们担心的正是这一点。他们想确保自己不会投入到一个即将过时的系统中,就像过去的许多 Web 开发框架那样。
那么:htmx 是框架吗?它是否会迅速过时,在其流星般消逝后留下一堆无法维护的网站?
尽管社区对此问题争论不休——我认为 htmx 显然是一个框架,至少在主要用例中如此。但这确实取决于你如何使用它。
在项目中使用 htmx 时,你会在 HTML 中包含 htmx 属性(如 hx-post
、hx-target
),编写由 htmx 格式化数据调用的端点(带有特定请求头),并返回 htmx 预期格式的数据(包含 hx-*
控件的 HTML)。所有这些属性、头部和端点相互作用,创建了一个通过网络请求使元素进出 DOM 的系统。
如果你使用 htmx 处理网站中大量非平凡的网络请求,那么 htmx 的引入将对项目结构产生重大影响,从前端标记的结构方式到端点的数据库查询。这是类框架行为,在此场景下,htmx 无法被轻松替换。
你当然可以以类库的方式使用 htmx,只为网页的少数部分添加动态功能。但React 也可以这样类库式使用,而没人会争辩 React 不是框架。可以说,许多在其应用中使用 htmx 的人都是在以框架的方式构建超媒体应用。
他们应该如此!遵循 htmx 的优势来构建效果会好得多。你可以发送 JSON 格式的表单体,如果你坚持。但你不该这么做!直接使用 application/x-www-form-urlencoded
格式并编写接受它们的端点更简单。你可以编写被多个不同客户端复用的端点,如果你坚持。但你不该这么做!将数据和超媒体 API 拆分为独立 URL 更简单。是的,htmx 可以作为库使用,但也让它成为你的框架吧。
然而,这并不意味着 htmx 只是另一个 JavaScript 框架,因为 htmx 有一个其他框架不具备的巨大优势:HTML。
假设你将 htmx 作为框架使用——它是 JavaScript 框架吗?在明显意义上,是的:htmx 由约 4k 行 JS 实现。但在更重要的意义上,它不是:React、Svelte、Solid 等框架让你编写 JS(X) 代码,然后由框架转换为 HTML;而 htmx 只让你编写 HTML。这消除了可能随时间推移让你放弃其他框架的整个维护类别。
代码库通常在你想升级或更改某些依赖项时陷入困境,但你使用的框架与该变更不兼容。Java 是最臭名昭著的例子——生产环境中有无数行 Java 代码永远停留在 Java 8,因为升级 Spring 太难——但 npm 包生态系统紧随其后。使用 htmx "框架"时,你永远不会遇到此问题,因为 htmx 是零依赖、客户端加载的 JavaScript 文件,因此保证永远不会与服务器依赖的任何构建过程或依赖链冲突。
浏览器渲染 HTML,因此使用 htmx 永远不需要编译器或转译器。虽然许多 htmx 用户乐于用 JSX 渲染 API 响应,但 htmx 与经典 模板 引擎配合得非常好,使其可移植到你喜欢的任何语言。无论你对 Django 和 Rails 有何看法,它们在 2008 年很重要,今天依然重要——htmx 与两者无缝集成。这是 htmx 驱动开发的反复出现的主题:htmx 与新旧开发工具都能良好协作,因为所有这些工具的共同点是 HTML,而 htmx 就是用来编写 HTML 的。
迫使用户主要在 HTML 而非 JS 中定义应用行为,有太多优势无法在此文中详述,所以我只谈人们最讨厌 JavaScript 框架的一点:剧变。根据你编写 React 应用的时间,你可能用受控类组件、React Hooks或这个实验性 <form>
扩展编写表单。这确实令人抓狂,尤其如果你像我一样最初是用类组件学习制作 Web 表单。
但无论你何时编写 htmx 应用,htmx 表单的行为始终与普通 HTML 表单大致相同:使用 <form>
。htmx 添加了额外的网络功能后,你终于可以使用 PUT
请求并控制响应去向,但在其他所有方面——验证、输入、标签、自动完成——你拥有默认的 <form>
元素行为。
最后,由于 htmx 仅在一个非常狭窄的领域(网络请求和 DOM 替换)扩展了 HTML,你编写的大部分"htmx"代码就是普通的 HTML。当你拥有复杂的状态管理机制时,实现一个自定义可折叠 div 非常简单;但如果你没有,你可能会停下来搜索<details>
元素。任何问题若能通过原生 HTML 元素解决,代码的寿命都将因此大幅提升。这是一种更友好的 Web 开发学习方式,因为你大部分知识在 HTML 存在期间都保持相关。
在这方面,htmx 更像 JQuery 而非 React(htmx 的前身intercooler.js 是 JQuery 扩展),但它通过声明式、基于 HTML 的接口改进了 JQuery:JQuery 要求你进入 <script>
标签指定 AJAX 行为,而 htmx 仅需一个简单的 hx-post
属性。
简而言之,虽然 htmx 可作为框架使用,但它是偏离 Web 语义远少于 JavaScript 框架的框架,并将受益于这些语义的改进,且无需用户额外工作,这要归功于 Web 的出色的向后兼容保证。如果你想构建一个持久的网站,这些特性使 htmx 成为比许多同代框架更可靠的选择。
注意:尽管同意此分析、在文中找不到逻辑缺陷并允许我在其网站上发布,Carson 仍坚持认为 htmx 是一个库。