何时应使用超媒体?

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 应用程序时的一个二选一决定。即使是最“单页”的 单页应用程序也毕竟使用了超媒体:作为启动应用程序的引导机制。

在他的演讲 《SPA 毁了 Web 吗》中,Rich Harris 给了我们 “过渡性”(Transitional)应用程序这个术语,即混合了超媒体和非超媒体(SPA)概念的应用程序。我们 已经在这里更详细地回应了 Harris 先生的演讲,但可以说我们 强烈同意他对 Web 开发采取务实的“过渡性”方法是最好的观点:你应该为正在处理的特定工作选择合适的工具。

我们可能与 Harris 先生存在分歧的地方在于“界限”在哪里——哪些功能可以在超媒体中有效实现, 哪些功能需要更复杂的客户端方法。我们认为,借助 htmx, 超媒体可以比当今许多 Web 开发者所认为的走得更远、远得多。而且,对于许多 应用程序,它可以满足其许多或全部 UX 需求。

超媒体:如果……则非常适合

…如果你的 UI 主要是文本 & 图像

《所有 htmx 演示之母》中,Contexte 的 David Guillot 展示了如何 用 htmx 替换 React 导致总代码库减少了 67%,以及其他许多令人瞩目的成果。

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

Contexte 如此完美地适合超媒体的原因是它是一个面向媒体的 Web 应用程序,显示供阅读的 文本和图像组成的文章。它有一个复杂的过滤机制和其他优点,但应用程序的核心是 显示和分类文章。这正是超媒体设计要做的事情,这就是为什么 htmx 和超媒体对他们的应用程序如此有效。

…如果你的 UI 是 CRUD 型的

超媒体取得长期成功的另一个领域是 CRUD 型 Web 应用程序,采用 Ruby on Rails 风格。如果你的主要应用程序机制是显示 表单并将表单保存到数据库中,那么超媒体可以很好地工作。

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

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

超媒体开始有点不稳定的一个领域是当你的 UI 依赖关系跨越屏幕的结构性 区域时。一个很好的例子,也是讨论超媒体方法时经常出现的,是显示在 GitHub “Issues” 标签页上的问题计数数字。很长一段时间, 当你在 GitHub 上关闭一个问题时,标签页上的问题计数不会正确更新。GitHub 总体上(尽管 不是唯一)使用的是超媒体风格的应用程序。

“啊哈!”,SPA 爱好者惊呼道,“看吧,连 GitHub 都搞不定这个!”

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

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

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

这个 UI 可以按以下方式布局:

嵌套示例

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

这里的技巧是电子邮件和电话计数在屏幕上与它们的集合共置,这允许你在对相应 集合进行修改时仅定位该特定区域进行更新。所有数据依赖项都共置在一个可以通过单一、简单 且明确的目标更新的单一区域内,而且,当它们被替换时不会相互干扰。

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

旁注: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 组件嵌入更大的超媒体 架构中通常比反过来更容易。孤立的客户端组件可以通过 事件与更广泛的超媒体应用程序通信,如 拖拽排序 Sortable.js + htmx 示例中所示。

…如果你想要集成的复制 & 粘贴组件

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

有前端库无关的组件库,如 lit,但它们不像 ShadCN 与 React 那样 与 htmx 集成。

…如果你的团队不支持

不选择超媒体的最后一个原因不是技术性的,而是社会性的:目前,超媒体在 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 的方式是其他方法所不具备的。

</>