为什么 Gumroad 没有选择 htmx

Sahil Lavingia

在 Gumroad,我们最近开始了一个名为 Helper 的新项目。作为 CEO,我最初对在这个项目中使用 htmx 非常乐观,即使有些团队成员对此并不那么热情。

我的乐观来自于之前使用 React 的经验,React 经常感觉对我们的需求来说过于复杂。我认为 htmx 可以成为一个很好的解决方案,可以让我们的前端保持超级轻量级。

Gumroad Red
使用 htmx 的示例 - 点击图片查看

事实上,我在 Slack 上与我们的团队分享了这种观点

“https://htmx.npmjs.net.cn/ 可能是添加简单交互的一种方式”

最初,它看起来很有希望!正如我们 Gumroad 的一位工程师所说

“HTMX(正式)是一个用来嘲讽 JS 生态系统变得多么复杂的笑话——就像 Tailwind 只是另一种语法来编写内联 CSS 一样,HTMX 只是另一种语法来编写内联 JS。”

然而,与 Tailwind 不同的是,Tailwind 在我们的工具集中找到了自己的位置,而 htmx 却没有满足我们的需求,也没有为我们的客户带来最佳的用户体验——至少在我们的用例中是如此。

以下是一些原因:

  1. 直觉和开发人员体验:虽然用 htmx 做正确的事情是可能的,但我们发现用 Next.js 让所有事情都正常运行更直观、更有趣。使用 Next.js 的开发过程感觉很自然,而使用 htmx,它往往感觉不自然、被迫。例如,在构建具有动态验证和条件字段的复杂表单时,我们发现自己编写了复杂的服务器端逻辑来处理 React 中原本简单的客户端操作。

  2. UX 限制:htmx 最终将我们的应用程序推向了 Rails/CRUD 方法,这导致了默认情况下非常糟糕(或者至少说,枯燥和通用的)用户体验。我们发现自己不断地与这种倾向作斗争,这是一种反生产的行为。例如,用 htmx 实现工作流构建器的拖放界面被证明是一个巨大的挑战,需要使用一些笨拙的解决方法,与我们使用 React 库可以实现的流畅体验相比,这些解决方法感觉很笨拙。

  3. AI 和工具支持:值得注意的是,AI 工具对 Next.js 非常熟悉,而对 htmx 的了解却不多,因为缺少开源训练数据。这类似于 Rails 面临的问题。虽然这不是一个决定性因素,但它确实影响了我们的开发速度以及查找解决方案的难易程度。当我们遇到问题时,React/Next.js 的丰富资源使故障排除变得更快。

  4. 可扩展性问题:随着项目复杂度的增加,我们发现 htmx 难以跟上我们的需求。最初吸引我们的简单性,当我们试图实现更复杂的交互和状态管理时,开始变得限制性。例如,当我们添加实时协作和复杂数据可视化等功能时,使用 htmx 的服务器端方法管理跨多个组件的状态变得越来越困难。

  5. 社区和生态系统:React/Next.js 生态系统庞大而成熟,为我们遇到的几乎所有问题都提供了解决方案。使用 htmx,我们经常发现自己重复造轮子或在功能方面妥协。当我们需要集成第三方服务和库时,这一点变得尤为明显,这些服务和库通常有 React 绑定,但没有 htmx 等效项。

Gumroad Green
使用 NextJS 的示例 - 点击图片查看

最终,我们转向了 React/Next.js,它非常适合构建我们一直在寻找的复杂 UX。我们对这个决定感到满意——目前为止。它使我们能够更快地行动,创造更具吸引力的用户体验,并利用大量现有的工具和库。

Gumroad Helper Before After
Gumroad Helper 之前和之后 - 点击图片查看

这种经历强化了一个宝贵的教训:虽然考虑轻量级替代方案很重要,但选择能够随着项目发展并支持长期愿景的技术同样重要。对于 Helper 来说,React 和 Next.js 证明了这种选择。

自从我们迁移到那里之后,我们已经能够为我们的核心客户认真升级应用程序的用户体验。

  1. 拖放功能:我们工作流构建器的关键功能之一是通过拖放重新排列步骤的能力。虽然使用 htmx 可以实现拖放,但我们发现现有的解决方案感觉很笨拙,需要大量的自定义 JavaScript。相比之下,React 生态系统提供了像 react-beautiful-dnd 这样的库,这些库提供了流畅、可访问的拖放,且设置最小。

  2. 复杂状态管理:每个工作流步骤都有自己的一组配置和条件逻辑。当用户编辑这些内容时,我们需要实时更新 UI 以反映更改以及它们对其他步骤的影响。使用 htmx,这将需要多次服务器往返或复杂的客户端状态管理,这违背了 htmx 的服务器端理念。React 的状态管理解决方案(如 useState 或更高级的选项,如 Redux)使这变得更加直观。

  3. 动态表单生成:每种步骤类型的配置都不同,并且可以根据用户输入而改变。使用 React 的组件模型生成这些动态表单并处理它们的状态更加直观。使用 htmx,我们发现自己编写了更复杂的服务器端逻辑来生成和验证这些表单。

  4. 实时协作:虽然在本截图中不可见,但我们实现了允许多个用户同时编辑工作流的功能。使用 WebSockets 和 React 实现这一点相对简单,而使用 htmx,则需要更复杂的服务器端逻辑和自定义 JavaScript 来处理实时更新。

  5. 性能优化:随着工作流规模越来越大、越来越复杂,我们需要对渲染优化进行细粒度控制。React 的虚拟 DOM 和像 useMemo 和 useCallback 这样的钩子使我们能够以 htmx 不那么容易获得或直观的方式优化性能。

值得注意的是,虽然使用 htmx 可以克服这些挑战,但我们发现解决这些挑战往往会让我们远离 htmx 的优势,转而使用在 JavaScript 密集型环境中感觉更自然的解决方案。这种认识是我们决定切换到 React 和 Next.js 的关键因素。

我们承认,htmx 可能非常适合许多项目,特别是那些具有更简单的交互模型或建立在现有服务器端渲染应用程序之上的项目。我们的经验并没有否定其他人从 htmx 中获得的益处。关键是了解项目特定的需求,并选择最符合这些需求的工具。

在我们的案例中,Helper 界面复杂的、有状态的性质使 React 和 Next.js 更适合。但是,我们仍然赞赏 htmx 的方法,并且可能会在未来的项目中考虑它,因为它的优势更符合我们的需求。

也就是说,我们始终愿意根据需求的变化和新技术的出现重新评估我们的技术栈。谁知道未来会发生什么?

</>