Rich Harris 是一位著名的 Web 开发者,他从事 Svelte.js 的工作,这是一个新颖的 单页应用 (SPA) 框架。
2021 年 10 月,他在 JamStack 上发表了一场演讲,标题为 “单页应用是否毁了网络?”。
我们被要求对这场演讲发表意见,因此这篇文章是我们的回应。
首先要说的是,这场演讲做得非常出色:制作精良、深思熟虑、幽默、公平对待辩论双方, 并且全程非常理性。我们不同意 Harris 先生所说的大部分内容,正如我们将在下面详细说明,但我们尊重 并欣赏他的观点以及他从事的技术。
演讲从对 SPA 的一些合理批评开始,特别是关注 Instagram(Facebook 朋友们的一个典型 SPA 实现)中发现的可用性问题。他非常公平地审视了 SPA 的缺点,包括但不限于以下列表:
在考虑 Instagram 的可用性问题后,Harris 先生这样说道:
来吧,大家。如果世界上最好的前端工程师都无法让 文本和图像在没有五兆字节 JavaScript 的情况下正常工作,那么也许 我们应该放弃 Web 平台。
在这里,我们与 Harris 先生完全同意,但有一个说明:我们会将 “JavaScript Web 平台” 替换为单纯的 “Web 平台”,因为 Instagram 使用的正是后者。
我们进一步澄清,SPA 应用和框架往往简单地 忽略 了 实际 的 Web 平台,即 Web 的原始 REST-ful 模型,除了作为引导机制之外。
Harris 先生随后转向多页应用 (MPA) 的问题,这些是 “传统” 的、 点击链接加载 HTML 页面的 Web 应用,我们都非常熟悉,并且在一定程度上, 它们正在被 SPA 取代。
下面,我们将逐一审视他概述的各种问题,所有这些问题都是 “标准” MPA 的特征, 并且我们将演示如何使用超媒体导向技术 htmx 的 MPA 来解决每一个问题。
标准 MPA 的一个普遍问题是它们在 每个请求时都会发出完整的页面刷新。这意味着像视频或音频播放器这样的内容将被替换,从而停止播放,当发出请求时。
这个问题可以通过 htmx 中的 hx-preserve 属性来解决,该属性告诉 htmx 在
请求之间保留特定内容。
在存在无限滚动行为(大概通过某种 JavaScript 实现)的情况下,后退按钮将无法与 MPA 正常工作。我要指出,无限滚动的存在会质疑 MPA 这个术语,因为传统上 MPA 会使用分页而不是无限滚动。
话虽如此,无限滚动 可以使用 htmx 以一种超媒体导向且明显的方式轻松实现。当与 hx-push-url 属性结合使用时,历史记录和后退按钮只需开发者很少的努力即可正常工作,所有这些都带有不错的“可复制粘贴”的 URL,有时被 SPA 社区中的人称为 “深度链接”。
优雅的过渡,当然,很不错。不过,我们认为设计师往往高估了它们对应用可用性的贡献。是的,演示很炫酷,但到第 20 次点击时,用户往往只是希望 UI 快点继续。
话虽如此,htmx 支持使用 标准 CSS 过渡 来实现动画。显然,使用这些纯 CSS 技术所能实现的效果有限,但我们相信这可以给你 80/20 情况中的 80。(或者,也许是 95/5 情况中的 95。)
Harris 先生大力强调 “糟糕的广告技术” 是 Web 上可用性问题的罪魁祸首,谁能为当今大多数网站向用户交付的 2.5MB 跟踪、间谍软件和广告软件负载辩护呢?Harris 先生指出,SPA 通过一次性加载这堆垃圾来缓解这个问题,而不是像 MPA 那样在每个请求上反复加载。
现在,一个普通的 MPA 通常会在第一次请求后缓存这些垃圾,因此至少下载成本与 SPA 相当。但 MPA 必须在每个页面 再次执行 这堆垃圾,这会消耗 CPU 并可能导致糟糕的用户体验。
然而,我们注意到,由 htmx 驱动的 MPA 与 SPA 具有完全相同的特性:广告垃圾将在第一次请求时下载并执行,此后,所有请求将是相对轻量级的 DOM 元素替换。
这是一个有效的观点:在 MPA 风格的应用中,你的 UI 交互受限于服务器响应请求的速度,即其延迟。其中一部分是网络延迟,没有中央数据存储这种传统 Web 应用极大简化的方面,就很难克服。不过,网络很快,并且越来越快,并且有已知的技术来优化 服务器 延迟(即服务器返回响应的速度),这些技术经过数十年发展,用于监控和优化此响应时间。SQL 调优、Redis 缓存等等,所有这些都已确立,并使亚 100ms 响应成为合理目标。许多 htmx 用户评论 htmx 基于的应用感觉多么快,但我们不会假装延迟不是一个需要考虑的问题。
当然,延迟问题的麻烦在于它们会让应用感觉迟钝。但是,像你一样,我们已经处理过大量迟钝的 SPA,因此我们必须说这个问题不能简单地通过采用 SPA 框架来整齐解决。此外,与服务器乐观同步数据可能会导致极难理解的数据一致性问题,以及整体应用复杂性的显著增加,我们稍后会回到这个话题。
GitHub 确实有 UI bug。然而,其中没有一个是特别难解决的。
htmx 提供了多种方式来 更新目标元素之外的内容,所有这些都非常简单,任何一种都能解决 Harris 先生指出的 UI 一致性问题。
将 GitHub 的 UI 问题与 Harris 先生早些时候指出的 Instagram UI 问题对比:Instagram 的问题将 需要更复杂的工程工作来解决。
Harris 先生随后讨论了 “过渡应用” 的概念,这些是 SPA 和 MPA 技术的混合。 这个术语是合理的,我们将看看这个术语是否会在行业中流行。
我们经常推荐在应用的部分使用 htmx 来保持简单,然后在需要时使用其他 技术:alpine.js、hyperscript、小型响应式 框架等。
因此,我们可以在一定程度上同意 Harris 先生在这里的观点,并推荐一种 “过渡” 的 Web 开发方法,尽管 我们会推荐在可能时倾向于 MPA/超媒体,而 Harris 先生似乎相当确定会倾向于 SPA/JavaScript。
不幸的是,有一个话题 Harris 先生没有讨论,我们相信这可能是因为他没有看到它。他是一位对该语言充满热情的 JavaScript 开发者,他浸润在前台框架的工程文化中,因此当前 JavaScript 前端开发的 复杂性 对他来说似乎是自然的。然而,对我们许多人来说,JavaScript 生态系统简直是 疯狂 过度复杂。实际上,这很可笑,鉴于大多数 Web 应用的需求。
Harris 先生随后提到的许多 “过渡” 技术:React Server Components(他称之为 “像通过线传输 HTML,但复杂得多”)、Marko(正在进行 “部分水合”)、Quik(显然积极懒加载东西),所有这些都是了不起的工程成就,但我们必须说,它们也相当复杂。
不幸的是,这是当前前端开发文化的一部分:应用框架中、构建工具链中、部署模型中等等,都容忍天花板般高的复杂性水平,并且,当由于所有这些复杂性而出现问题时,往往会提供更多复杂性作为答案。
“简单” 被视为贬义,而 “复杂” 则是高度赞扬。
这种复杂性正在压倒当今的许多开发者和开发团队。正如 Harris 先生自己在讨论 Instagram 时指出的,即使是 世界上一些最好的前端工程师似乎也无法控制这一切。
因此,这里有一个文化问题。
也有一个技术问题。
这个问题可以总结为 “超媒体方法” 与 “远程过程调用 (RPC) 方法”。
当 Web 应用从 MPA 转向 SPA 时,它们往往在不知不觉中采用了 RPC 方法来开发应用: AJAX 转向 JSON 作为数据序列化格式,并很大程度上(并且正确地) 放弃了超媒体概念。这种对超媒体方法的放弃是由普通的 MPA 的公认可用性问题驱动的。
然而,结果证明,那些可用性问题往往 可以 通过超媒体方法解决: 而不是 放弃 超媒体转向 RPC,我们当时需要并且今天需要的是一种 更强大 的超媒体。
这正是 htmx 提供的。
通过回归超媒体方法,你可以构建合理复杂的 Web 应用,这些应用解决了 Harris 先生关于 MPA 的许多担忧,而所需复杂性仅为大多数流行 SPA 框架所需的一小部分。此外,不用多想,你就会获得 Roy Fielding 概述的真正 REST-ful 架构的所有 好处。
超媒体架构适合所有 Web 应用吗?显然不是。
它适合许多,或许大多数 Web 应用吗?我们当然认为如此,至少在一定程度上。
现在我们来到了演讲中最具情感色彩的声明: “JavaScript 的船已经扬帆远航”,并且 我们应该接受它将成为未来 Web 开发中的主导编程语言。
Harris 先生相信,将是 边缘计算 成为 最终消除对 JavaScript 剩余、分散反对的驱动力。
我们对此并不那么确定。
相反,我们不期望边缘计算在可预见的未来在大多数 Web 应用中发挥作用。 或者,直白地说,永远不会。CPU 很便宜,网络速度很快并且在增加,微服务是一团乱麻。
而且,与 Harris 先生所说相反,今天的 趋势并不明显有利于 JavaScript。五年前,我们作为 JavaScript 抵抗运动的创始成员 对阻止 JavaScript 巨轮的任何希望感到绝望。但然后发生了一些 意想不到的事情:Python 起飞了,同时 JavaScript 平平:

JavaScript 在 2010 年代中期达到峰值的趋势也可以在 GitHub 上观察到:

现在,这是否意味着 JavaScript 最终会 “输给” Python 并消失?
当然不是。JavaScript 是 Web 的核心技术,将与我们永远在一起。没有它,我们无法构建 htmx(或 hyperscript),所以我们非常感谢 JavaScript。
但这 确实 意味着 Web 的未来并不 必然 完全属于 JavaScript,就像五年前 似乎是那样。
我们喜欢谈论 HOWL 栈:Hypermedia On Whatever you’d Like。想法是,通过回归(更强大)的超媒体架构,你可以使用任何你喜欢的后台语言:Python、Lisp、Haskell、Go、Java、C#,随便什么。甚至 JavaScript,如果你喜欢。
由于你使用超媒体和 HTML 进行服务器交互,你不会感受到采用后台 JavaScript 的压力,而一个巨大的 JavaScript 前端会产生这种压力。当然,你仍然可以使用 JavaScript(也许以 alpine.js 的形式), 但你以它最初预期的用途使用它:作为一种轻量级的前端脚本语言来增强你的 应用。或者,如果你足够勇敢,也许你可以尝试 hyperscript 来满足这些需求。
这是一个我们更喜欢的世界:许多编程语言选项,每种都有自己的优势、技术文化和繁荣 社区,所有这些都能通过更强大的超媒体的魔力参与 Web 开发世界,而不是 一个 SPA 与 Node 通过 JSON 对话的单一体。多样性,毕竟,是我们的力量。
总之,
