Rich Harris 是一位知名的网页开发者,他参与了 Svelte.js 的开发,Svelte.js 是一款新颖的单页应用 (SPA) 框架。
在 2021 年 10 月,他在 JamStack 上发表了名为 “单页应用毁掉了网络吗?” 的演讲。
我们被问到我们对这个演讲的看法,所以这篇论文是我们的回应。
关于这个演讲的第一点要说的就是它做得非常出色:制作精良、思考周全、有趣、对辩论的双方都公平公正,而且始终非常合理。我们并不完全同意 Harris 先生的观点,正如我们将在下面详细说明的那样,但我们尊重和欣赏他的观点以及他所参与开发的技术。
演讲从对 SPA 的一些合理的批评开始,特别是关注了在 Instagram 中发现的可用性问题,Instagram 是我们 Facebook 朋友的典型 SPA 实现。他对 SPA 的缺点进行了非常公平的分析,包括但不限于以下列表
在考虑了 Instagram 的可用性问题之后,Harris 先生这样说
拜托大家。如果世界上最优秀的前端工程师都无法在没有 5 兆字节 JavaScript 的情况下让文本和图像正常工作,那么也许我们应该放弃网络平台。
在这里,我们强烈同意 Harris 先生的观点,但我们要补充一点,我们会将“JavaScript 网络平台”替换为“网络平台”,因为 Instagram 所使用的是网络平台。
我们还要澄清一点,SPA 应用程序和框架通常只是忽略了实际的网络平台,即网络的原始 RESTful 模型,除了作为启动机制之外。
Harris 先生接着谈到了多页应用程序 (MPA) 的问题,MPA 是我们都熟悉且在一定程度上被 SPA 取代的“传统”的点击链接-加载 HTML 页面式 Web 应用程序。
下面,我们将逐一介绍他提出的各种问题,所有这些问题都适用于“标准”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 先生将重点放在“糟糕的广告技术”是网络可用性问题的罪魁祸首,而谁又能为当今大多数网站向用户提供的 2.5 兆字节的跟踪、间谍软件和广告软件负载辩护呢?Harris 先生指出,SPA 通过一次加载这个垃圾包来改善了这个问题,而不是像 MPA 那样在每次请求时都加载一遍。
现在,一个普通的 MPA 通常会在第一次请求后对垃圾进行缓存,因此至少下载成本与 SPA 相同。但 MPA 必须在每个页面上再次执行垃圾包,这会消耗 CPU 并可能导致用户体验不佳。
然而,我们注意到,由 htmx 支持的 MPA 与 SPA 具有完全相同的特征:广告垃圾会在第一次请求时被下载并执行,此后,所有请求都将是相对轻量级的 DOM 元素替换。
这是一个有效的观点:在 MPA 风格的应用程序中,你的 UI 交互受限于服务器响应请求的速度,即它的延迟。其中一部分是网络延迟,要克服这个问题,就必须放弃传统 Web 应用程序的极大简化方面之一:集中式数据存储。然而,网络速度很快,而且正在变得越来越快,并且经过几十年的发展,存在着优化服务器延迟(即服务器返回响应的速度)的知名技术,用于监控和优化这种响应时间。SQL 优化、Redis 缓存等等,这些技术都已成熟,使 100 毫秒以内的响应成为一个合理的目标。许多 htmx 用户注意到 htmx 驱动的应用程序运行速度非常快,但我们不会假装延迟不是一个需要考虑的问题。
当然,延迟问题会导致应用程序感觉滞后。但就像你一样,我们也使用过许多滞后的 SPA,因此我们不得不说,这个问题并没有通过简单地采用 SPA 框架就能得到解决。最重要的是,乐观地与服务器同步数据会导致非常难以理解的数据一致性问题,以及应用程序总体复杂性的显著增加,我们将在后面讨论这个问题。
GitHub 确实存在 UI 错误。然而,这些错误都不难解决。
htmx 提供了多种方法来 更新目标元素之外的内容,所有这些方法都非常简单,任何一种方法都可以解决 Harris 先生指出的 UI 一致性问题。
将 GitHub 的 UI 问题与 Harris 先生之前指出的 Instagram 的 UI 问题进行对比:Instagram 的问题需要更加复杂的技术工作才能解决。
Harris 先生接着讨论了“过渡型应用程序”的概念,这种应用程序融合了 SPA 和 MPA 技术。这个术语很合理,我们将看看这个术语是否会在业界流行。
我们通常建议在应用程序的某些部分使用 htmx,因为这样可以保持简单,然后在需要时使用其他技术:alpine.js、hyperscript(一个小型响应式框架)等等。
所以,在某种程度上,我们可以同意 Harris 先生的观点,并建议采用“过渡型”方法进行网页开发,尽管我们会建议在可能的情况下倾向于 MPA/超媒体,而 Harris 先生似乎相当肯定地倾向于 SPA/JavaScript。
不幸的是,Harris 先生没有讨论一个主题,我们认为这可能是因为他没有看到它。他是一位 JavaScript 开发者,他对这门语言充满热情,并且沉浸于前端框架的工程文化中,因此,当今 JavaScript 前端开发的复杂性在他看来是自然的。然而,对于我们中的许多人来说,JavaScript 生态系统简直是疯狂地过于复杂。事实上,考虑到大多数 Web 应用程序的要求,这简直是可笑。
Harris 先生继续提到的许多“过渡型”技术:React 服务器组件(他称之为“类似于通过网络传输 HTML,但要复杂得多”)、Marko(它正在进行“部分水合”)、Quik(它积极地延迟加载内容,显然如此),所有这些都是非凡的工程成就,但我们必须说,它们也都很复杂。
不幸的是,这是当今前端开发文化的一部分:在应用程序框架、构建工具链、部署模型等等方面,都容忍着极高的复杂性,而且当由于所有这些复杂性而出现问题时,更复杂的解决方案通常被视为答案。
“简单”被贬低,“复杂”被赞扬。
这种复杂性正在压倒当今许多开发者和开发团队。正如 Harris 先生自己在讨论 Instagram 时指出的那样,即使是一些世界上最优秀的前端工程师似乎也无法控制这一切。
所以这里存在着文化问题。
也存在着技术问题。
这个技术问题可以概括为“超媒体方法”与“远程过程调用 (RPC) 方法”。
当 Web 应用程序从 MPA 转向 SPA 时,它们在不知不觉中采用了 RPC 方法进行应用程序开发:AJAX 转向 JSON 作为数据序列化格式,并且在很大程度上(而且正确地)放弃了超媒体的概念。这种对超媒体方法的放弃是由对普通 MPA 的可用性问题的承认所驱动的。
然而,事实证明,这些可用性问题通常可以通过使用超媒体方法来解决:而不是抛弃超媒体而使用 RPC,我们当时需要,现在也需要的是更强大的超媒体。
这正是 htmx 为你提供的。
通过回归超媒体方法,你可以构建相当复杂的 Web 应用程序,以较少的复杂度解决 Harris 先生对 MPA 的许多担忧,而大多数流行的 SPA 框架则需要更高的复杂度。此外,无需过多考虑,你将获得 Roy Fielding 关于真正 RESTful 架构的所有好处。
超媒体架构适合所有 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-to-Node-in-JSON 的单一体系。毕竟,多样性是我们的力量。
总之,