对"单页应用是否毁了Web?"的回应

Carson Gross

Rich Harris是一位知名的Web开发人员,致力于Svelte.js的开发,这是一个新颖的单页应用(SPA)框架。

2021年10月,他在JamStack上发表了一场题为"单页应用是否毁了Web?"的演讲。

有人询问我们对这场演讲的看法,因此本文就是我们的回应。

关于这场演讲首先要说的是它做得非常好:制作精良、深思熟虑、幽默、对辩论双方都很公平,并且整个过程非常合理。我们不同意Harris先生的许多观点,如下文所述,但我们尊重并欣赏他的观点以及他所从事的技术。

SPA的问题

演讲以对SPA的一些合理批评开始,特别关注了Instagram(来自Facebook的朋友们的一个典型SPA实现)中发现的可用性问题。他非常公正地看待了SPA的缺点,包括但不限于以下列表:

在考虑了Instagram的可用性问题后,Harris先生这样说:

拜托各位。如果世界上最优秀的前端工程师都无法在没有5兆字节JavaScript的情况下让文本和图像正常工作,也许我们应该放弃Web平台。

在这里,我们与Harris先生达成了强烈共识,但有一个前提:我们会将"Web平台"替换为"JavaScript Web平台",因为Instagram的情况正是如此。

我们进一步澄清,SPA应用和框架通常只是简单地忽略真正的Web平台,即Web原始的、REST风格模型,除了作为引导机制。

MPA的问题

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。)

"多页应用每次请求都加载JavaScript库"

Harris先生将"糟糕的广告技术"作为Web可用性问题的罪魁祸首,谁能为今天大多数网站向用户提供的2.5MB跟踪、间谍软件和广告软件的负载辩护呢?Harris先生指出,SPA通过一次性加载这堆垃圾而不是像MPA那样每次请求都加载,从而缓解了这个问题。

现在,普通的MPA通常会在第一次请求后缓存这堆垃圾,因此下载成本至少与SPA相同。但MPA必须在每个页面上再次执行这堆垃圾,这确实会消耗CPU并可能导致糟糕的用户体验。

然而,我们注意到,由htmx驱动的MPA具有与SPA完全相同的特性:广告垃圾会在第一次请求时下载并执行一次,之后所有请求都将是相对轻量级的DOM元素替换。

"MPA有网络延迟问题"

这是一个合理的观点:使用MPA风格的应用程序,你的UI交互受限于服务器响应请求的速度,即其延迟。其中一部分是网络延迟,如果不放弃传统Web应用程序的一个极大简化方面:集中式数据存储,就很难克服。然而,网络速度很快并且越来越快,并且有众所周知的优化服务器延迟(即服务器返回响应的速度)的技术,经过几十年的发展,用于监控和优化这个响应时间。SQL调优、Redis缓存等等,都使得低于100毫秒的响应成为一个合理的目标。许多htmx用户评论说基于htmx的应用程序感觉多么快速,但我们不会假装延迟不是一个需要考虑的问题。

当然,延迟问题的关键在于它会让应用感觉迟缓。但是,像你一样,我们也使用过许多迟缓的SPA,所以我们必须说这个问题并不能简单地通过采用SPA框架来解决。除此之外,乐观地与服务器同步数据可能导致极难理解的数据一致性问题,以及整体应用复杂性的显著增加,这个话题我们稍后会再讨论。

"GitHub有UI错误"

GitHub确实有UI错误。然而,它们都不是特别难解决。

htmx提供了多种方法来更新目标元素之外的内容,所有这些方法都非常简单,任何方法都可以解决Harris先生指出的UI一致性问题。

将GitHub的UI问题与Harris先生之前指出的InstagramUI问题进行对比:Instagram的问题需要更复杂的工程工作来解决。

过渡性应用

Harris先生随后讨论了"过渡性应用"的概念,即SPA和MPA技术的混合体。这个术语是合理的,我们将看看这个术语是否会在行业中流行。

我们经常建议在适合保持简单的地方使用htmx,然后在需要时使用其他技术:alpine.jshyperscript、一个小型响应式框架等。

因此,我们可以在一定程度上同意Harris先生的观点,并推荐一种"过渡性"的Web开发方法,尽管我们会尽可能倾向于MPA/超媒体,而Harris先生似乎肯定会倾向于SPA/JavaScript。

房间里的大象:复杂性

不幸的是,有一个话题Harris先生没有讨论,我们认为这可能是因为他没有看到它。他是一位对JavaScript充满热情的JavaScript开发人员,沉浸在前端框架的工程文化中,因此当前JavaScript前端开发的复杂性对他来说似乎是自然的。然而,对我们许多人来说,JavaScript生态系统简直疯狂地过于复杂。事实上,考虑到大多数Web应用的需求,这很可笑。

Harris先生随后提到的许多"过渡性"技术:React服务器组件(他称之为"像HTML over the wire,但复杂得多")、Marko(正在进行"部分水合")、Quik(显然积极懒加载东西),都是了不起的工程成就,但我们也必须说,它们都非常复杂。

不幸的是,这是当前前端开发文化的一部分:在应用框架、构建工具链、部署模型等方面容忍极高的复杂性,而当所有这些复杂性导致问题时,通常会用更多的复杂性作为答案。

"简单"被轻视,而"复杂"则备受赞誉。

这种复杂性正在压倒许多开发人员和开发团队。正如Harris先生在讨论Instagram时指出的那样,即使是世界上一些最优秀的前端工程师似乎也无法控制这一切。

所以这里有一个文化问题。

也有一个技术问题。

这个技术问题可以概括为"超媒体方法"与"远程过程调用(RPC)方法"。

当Web应用从MPA转向SPA时,它们往往无意中采用了RPC方法进行应用开发:AJAX转向JSON作为数据序列化格式,并基本上(并且正确地)放弃了超媒体的概念。这种对超媒体方法的放弃是由普通MPA公认的可用性问题驱动的。

然而,事实证明,这些可用性问题通常可以通过超媒体方法解决:与其放弃超媒体转向RPC,我们当时和现在需要的是一个更强大的超媒体。

这正是htmx提供的。

通过回归超媒体方法,你可以构建相当复杂的Web应用,以解决Harris先生关于MPA的许多担忧,而所需复杂性只是大多数流行SPA框架的一小部分。此外,无需过多思考,你将获得Roy Fielding概述的真正REST架构的所有好处

超媒体架构是否适合所有Web应用?显然不是。

它是否适合许多,也许是大多数Web应用?我们当然这么认为,至少部分适用。

JavaScript:抵抗运动

现在我们来到演讲中最情绪化的主张:关于JavaScript的"船已经起航",我们应该接受它将成为未来Web开发中的主导编程语言。

Harris先生认为,边缘计算将成为最终消除剩余、分散的对JavaScript抵制的驱动力。

我们对此不太确定。

相反,我们不认为边缘计算会在可预见的未来(或者坦率地说,永远)在大多数Web应用中发挥作用。CPU很便宜,网络速度很快并且在提高,微服务是一团糟。

而且,与Harris先生所说的相反,今天趋势显然不利于JavaScript。五年前,作为JavaScript抵抗运动的创始成员,我们对阻止JavaScript巨轮感到绝望。但随后发生了意想不到的事情:Python起飞了,与此同时,JavaScript停滞不前:

JavaScript开发者

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

JavaScript开发者

现在,这是否意味着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-用-JSON-与-Node-对话的单一文化。毕竟,多样性是我们的力量。

最后,

JavaScript开发者

</>