htmx 最常见的批评之一,通常来自第一次听说它的人,是这样的:
你抱怨现代前端框架的复杂性,但你的解决方案只是另一个复杂的前端框架。
这是一个极好的反对意见!这是关于 any 引入到项目中的第三方 (3P) 代码时应该提出的正确问题。即使你没有自己编写 3P 代码,通过将其包含在项目中,你就承诺要理解它——如果你想升级它,还需要刷新这种理解。这是一个巨大的承诺。
让我们将这个批评分解成其组成部分,并确定 htmx 究竟在多大程度上纵容了它声称要解决的危害。
一些 htmx 的捍卫者会跳出来帮助我们:“htmx 不是框架,它是一个库。”这可能是不正确的。
“框架”是一个口语化术语——没有硬性规则来定义第三方代码从“库”演变为“框架”的确切点——但我们仍然应该尝试定义它。在这个上下文中:
如果你喜欢隐喻:库是你添加到机器中的一个齿轮,框架是一个预构建的机器,你通过自定义其齿轮来控制它。
这种区别虽然模糊,但很重要,因为它描述了某些第三方代码有多容易被替换。例如,一个使用 CSV 解析库的 JavaScript 服务可能可以轻松地换成另一个 CSV 解析库;然而,一个使用 NextJS 框架的 JavaScript 服务,可能在其整个有用寿命中都依赖 NextJS,因为大量代码都是基于与 NextJS 结构交互的假设编写的。
因此,如果你的服务建立在框架之上,其有用寿命就与该框架的有用寿命挂钩。如果该框架被废弃、被厌恶,或者否则不适合工作,你的项目的修改难度将稳步增加,直到你放弃修改它,并最终将其彻底封存。
这就是当人们问“htmx 只是另一个 JavaScript 框架吗?”时他们所担心的。他们想确保自己没有承诺一个很快就会过时的系统,就像过去许多网络开发框架一样。
那么:htmx 是一个框架吗?它会很快被淘汰,从而在其陨落中留下一堆无法维护的网站吗?
对我们社区关于这个问题的持续辩论表示歉意——我认为 htmx 显然是一个框架,至少在大多数用例中是这样。但这取决于你如何使用它。
无论你在项目中哪里使用 htmx,你都会在你的 HTML 中包含 htmx 属性(即 hx-post、hx-target),编写被 htmx 格式化数据调用的端点(带有特定请求头),并从那些端点返回 htmx 期望的格式数据(带有 hx-* 控件的 HTML)。所有这些属性、头和端点相互交互,创建一个系统,通过网络请求让元素进入和退出 DOM。
如果你使用 htmx 处理网站的大量网络请求,那么在应用中包含 htmx 会对项目的结构产生重大影响,从你构建前端标记的方式,到你的端点进行的数据库查询。这就是框架-like 的行为,在这种情况下,htmx 无法被简单替换。
你当然可以以库-like 的方式使用 htmx,为网页的几个部分添加动态功能。但你也可以以这种库-like 的方式编写 React,而且没有人争论 React 不是一个框架。简而言之,许多在应用中使用 htmx 的人都是以一种屈从于 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 框架的一个方面: churn。根据你编写 React 应用的时间,你可能用受控类组件编写了你的表单,或者react hooks,或者这个实验性的 <form> 扩展。这确实令人抓狂,尤其是如果你——像我一样——首先用类组件学习如何制作网络表单。
然而,无论你何时编写 htmx 应用,htmx 表单的行为总是以大致与常规 HTML 表单相同的方式定义:使用 <form>。htmx 添加额外的网络功能,你终于可以使用 PUT 请求并控制响应去向,但是在所有其他方面——验证、输入、标签、自动完成——你都有默认的 <form> 元素行为。
最后,因为 htmx 只是非常狭窄领域(网络请求和 DOM 替换)中扩展 HTML,你编写的“htmx”大部分只是普通的 HTML。当你有访问复杂状态管理机制时,实现自定义可折叠 div 变得非常容易;当你没有时,你可能会停下来搜索 <details> 元素。每当一个问题可以通过原生 HTML 元素解决时,代码的寿命就会大大提高。这是一种学习网络开发 гораздо更不疏离的方式,因为你的知识大部分将与 HTML 一样保持相关。
在这方面,htmx 更像 JQuery 而非 React(htmx 的前身,intercooler.js,是一个 JQuery 扩展),但它通过使用声明式、基于 HTML 的接口改进了 JQuery:JQuery 让你去 <script> 标签指定 AJAX 行为,htmx 只需一个简单的 hx-post 属性。
简而言之,虽然 htmx 可以作为框架使用,但它是一个远不如 JavaScript 框架偏离网络语义的框架,并且将受益于这些语义的改进,而无需用户额外工作,多亏了网络的优秀的向后兼容性保证。如果你想构建一个持久很久的网站,这些品质使 htmx 成为比许多同代产品更好的选择。
注:尽管同意这个分析,在文章中没有发现逻辑缺陷,并允许我在他的网站上发布它,Carson 仍然坚持认为 htmx 是一个库。