何时应该使用超媒体?

Carson Gross

不过,这种权衡是,统一的接口会降低效率,因为信息是以标准形式传输的,而不是针对应用程序特定需求的特定形式。REST 接口旨在高效传输大粒度超媒体数据,针对 Web 的常见情况进行优化,但导致的接口对于其他形式的架构交互并非最优。

-Roy Fielding, https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm#sec_5_1_5

我们显然是超媒体的粉丝,并认为它至少可以部分解决当今 Web 开发世界面临的许多问题:

借助 htmx 及其提供的额外 UX 可能性,我们相信许多现代 Web 应用程序可以使用 HTML 和超媒体范式构建。

话虽如此,正如所有技术选择一样,超媒体也伴随着权衡取舍。在本文中,我们将为您提供一些思考方式,帮助您判断超媒体是否适合您正在构建的应用程序或功能。

过渡式应用程序与超媒体

在我们深入讨论超媒体何时是好选择之前,我们想澄清一点:采用超媒体并不是在构建 Web 应用程序时非此即彼的决定。毕竟,即使是最“单页式”的单页应用程序也会使用超媒体:作为引导机制来启动应用程序。

在 Rich Harris 的演讲 Have SPAs Ruined The Web 中,他给我们引入了“过渡式”应用程序的概念,即混合使用超媒体和非超媒体(SPA)概念的应用程序。我们在此更详细地回应了 Harris 先生的演讲,但简而言之,我们强烈同意他的观点:务实的“过渡式” Web 开发方法是最好的:您应该为特定任务使用正确的工具。

我们可能与 Harris 先生的分歧在于“界限”在哪里,即哪些功能可以在超媒体中有效实现,而哪些功能需要更复杂的客户端方法。我们认为,借助 htmx,超媒体可以远超当今许多 Web 开发人员认为的可能范围。而且,对于许多应用程序来说,它可以满足许多或所有的 UX 需求。

超媒体:适合的情况,如果……

…如果您的 UI 主要由文本和图像组成

The Mother Of All htmx Demos 中,Contexte 的 David Guillot 展示了如何将 React 替换为 htmx,导致总代码库减少 67%,以及其他令人震惊的结果。

尽管我们很想声称每个从 React 转向 htmx 的团队都会获得这些结果,但事实是 Contexte 的 Web 应用程序极度适合超媒体风格。

Contexte 如此适合超媒体的原因是,它是一个面向媒体的 Web 应用程序,用于显示由文本和图像组成的文章供阅读。它具有复杂的过滤机制和其他便利功能,但应用程序的核心是显示和分类文章。这正是超媒体设计用于实现的事情,这就是为什么 htmx 和超媒体在他们的应用程序中效果如此出色。

…如果您的 UI 是 CRUD 式的

超媒体在 CRUD 式 Web 应用程序中拥有悠久的成功记录,这种风格类似于 Ruby on Rails。如果您的主要应用程序机制是显示表单并将表单保存到数据库中,那么超媒体可以非常有效。

而且,借助 htmx,它也可以非常流畅,而不仅仅局限于许多服务器端应用程序采用的简单列表视图/详情视图方法。

…如果您的 UI 是“嵌套式”的,更新主要发生在明确定义的块内

超媒体开始有些摇晃的一个领域是,当您有跨越屏幕结构区域的 UI 依赖关系时。一个很好的例子,也是讨论超媒体方法时经常出现的,是 GitHub 中“Issues”选项卡上显示的问题计数“Issues”选项卡。很长一段时间,当您在 GitHub 上关闭一个问题时,选项卡上的问题计数没有正确更新。GitHub 总体上(尽管并非完全)使用超媒体风格的应用程序。

“啊哈!”,SPA 爱好者惊呼,“看,连 GitHub 都做不到!”

嗯,GitHub 已经修复了这个问题,但它确实展示了超媒体方法的一个问题:如何干净地更新 UI 的不连续部分?htmx 提供了一些让这起作用的技术,而在他们的演讲中,Contexte 讨论了使用事件方法非常干净地处理这种情况。

但是,让我们承认这是一个超媒体方法可能陷入困境的领域。为了避免这个问题,一种潜在策略是将给定资源的依赖元素共置在屏幕上的特定区域或区域中。

例如,考虑一个联系人应用程序,其显示和编辑联系人的详情屏幕具有:

此 UI 可以按以下方式布局:

Nested Example

在这种场景中,每个子部分都可以有自己的专用超媒体端点:

这里的诀窍是,电子邮件和电话计数与它们的集合在屏幕上共置,这允许您在对相应集合进行修改时仅针对该特定区域进行更新。所有数据依赖关系都共置在可以通过单个、简单且明显的靶点更新的单一区域内,而且在替换时它们不会相互干扰。

每个区域有效地形成一种服务器端组件,与屏幕上的其他区域独立,并且它们都嵌套在更广泛的联系人详情用户界面中。

旁注:UI 驱动的超媒体 API

请注意,在这种情况下,我们的超媒体 API(即我们的端点)是_由 UI 驱动的_:我们有一个特定的 UI 布局想要实现,并将我们的 API 适应它。如果 UI 改变了,我们会毫不犹豫地完全更改我们的 API 以满足新要求。这是使用超媒体开发的独特方面,我们在此更详细讨论

当然,可能有 UI 要求不允许以这种方式对依赖元素进行分组,如果上述技术不令人满意,那么可能是时候考虑替代方法了。

…如果您需要“深链接”和良好的首次渲染性能

超媒体优于其他选项的最后一个领域是,当您需要“深链接”时,即进入应用程序的链接超出着陆页,或者当您需要优秀的首次渲染性能时。

由于超媒体是 Web 的自然语言,并且浏览器在给定 URL 时非常擅长渲染 HTML,使用这种方法对于诸如这些的“传统” Web 功能来说很难被击败。

超媒体:不适合的情况,如果……

…如果您的 UI 具有许多动态相互依赖关系

如我们在“嵌套”UI 部分中讨论的,超媒体可能遇到麻烦的一个领域是,当您的 UI 中有许多散布的 UI 依赖关系,并且您无法“更新整个 UI”时。这就是 Roy Fielding 在本文开头引述中所指出的:Web 是为大粒度超媒体数据传输设计的,而不是为大量小数据交换设计的。

特别是对于超媒体难以处理的是,当这些依赖关系是动态的时,即它们依赖于无法在服务器端渲染时确定信息。一个很好的例子是像电子表格这样的东西:用户可以输入任意函数到单元格中,并即时引入屏幕上的各种依赖关系。

(注意,不过,对于许多应用程序来说,“可编辑行” 模式是更通用电子表格式行为的合适替代方案,这种模式通过将编辑隔离在有界区域内,与超媒体配合良好。)

…如果您需要离线功能

超媒体分布式架构严重依赖服务器端来渲染资源的表示。当服务器宕机或不可达时,该架构显然会遇到麻烦。可以使用 Service Workers 处理离线请求(尽管这是一个复杂选项),并且很容易检测超媒体应用程序何时离线并显示离线消息,就像许多厚客户端应用程序所做的那样。

但是,如果您的应用程序需要在离线环境中实现完整功能,那么超媒体方法将不是一个可接受的选择。

…如果您的 UI 状态更新极其频繁

超媒体不是好方法 的另一个情况是,如果您的 UI 状态更新频繁。一个很好的例子是需要捕获鼠标移动的在线游戏。在鼠标移动和 UI 更新之间插入超媒体网络请求将无法正常工作,您会更好地为游戏编写自己的客户端状态管理,并使用不同技术与服务器同步。

当然,您的游戏可能还有一个设置页面,而该设置页面可能比您用于游戏核心的任何解决方案都更适合使用超媒体。混合方法没有任何问题,以过渡式风格!

然而,我们应该注意到,通常更容易将 SPA 组件嵌入到更大的超媒体架构_中_,而不是反之。隔离的客户端组件可以通过事件与更广泛的超媒体应用程序通信,如 drag-and-drop Sortable.js + htmx 示例所示。

…如果您想要集成复制粘贴组件

最近,我们看到了“复制粘贴”友好组件的兴起,例如 ShadCN。这些组件通常是为特定前端框架(如 React)设计的,选择 htmx 意味着您无法使用它们。

有像 lit 这样的前端库中立组件库,但它们与 htmx 的集成不如 ShadCN 与 React 的集成那样紧密。

…如果您的团队不支持

不选择超媒体的最后一个原因不是技术性的,而是社会学的:目前,超媒体在 Web 开发中并不受欢迎。许多公司已将 React 采用为构建 Web 应用程序的标准库。许多开发者和顾问已将他们的职业押注在其上。许多招聘经理从未听说过超媒体,更不用说 htmx 了,但出于习惯在每个职位中放置 React。招聘当然更容易!

虽然这令人沮丧,但这是一个真实现象,应该谦虚地牢记。尽管 Contexte 能够快速有效地用 htmx 重写他们的应用程序,但并非所有团队都那么小、敏捷和热情,也并非所有应用程序都是这种方法的绝佳选择。可能最好先在边缘采用超媒体,或许先用于内部工具,以证明其价值,然后再更广泛地审视它。

结论

我们经常被问到:“好吧,那么 htmx 不适合 哪些类型的应用程序?”我们更喜欢使用“过渡式”应用程序概念按功能逐一思考,但当思考超媒体与其他方法相比能实现多少时,有一些广泛的流行应用程序作为参考很有用。

为了举例,考虑两个我们认为_可以_干净地在超媒体中实现的著名应用程序:TwitterGMail。这两个 Web 应用程序都是文本和图像密集型,具有粗粒度更新,因此非常适合超媒体方法。

两个不适合超媒体方法的著名 Web 应用程序示例是 Google SheetsGoogle Maps。Google Sheets 可以有大量状态在许多单元格内以及单元格之间的相互依赖关系,使得在每个单元格更新时发出服务器请求不可行。另一方面,Google Maps 对鼠标移动做出快速响应,并且无法负担每个鼠标移动的服务器往返。这两个应用程序都需要比超媒体所能提供的更复杂的客户端设置。

当然,绝大多数 Web 应用程序远未达到这些示例的规模和复杂性。几乎每个 Web 应用程序,即使是 Google Sheets 或 Google Maps,也都有部分潜在地更适合超媒体方法:更简单、更快、更干净。

将超媒体作为工具箱中的工具,将提升您作为 Web 开发者解决工程问题的能力,即使它没有成为您最喜欢的锤子。该方法有良好的理论基础对许多应用程序的实际益处,并且以其他方法无法比拟的方式“顺应”Web 的纹理。

</>