服务器端路由的回归 多页应用程序 (MPA) HTML 框架 服务器组件分析 结论

2025-05-28

服务器端路由的回归

多页应用程序(MPA)

HTML 框架

服务器组件

分析

结论

回归?它从未消失。或者至少某些自鸣得意的“早就告诉你了”的人会这么说。但对于那些过去十年并非与世隔绝的人来说,无论好坏,大部分网站都已经转向客户端导航了。

这一趋势得益于支持此类架构的工具的采用。“现代”JavaScript 框架旨在构建应用程序。单页应用程序。这个名字源于它不需要返回后端服务器来在页面之间导航。所有路由都在浏览器中进行。

它始于 Web 应用程序,但 React、Angular、Vue 等已经渗透到所有行业和各种类型的 Web 体验中,从最成功的科技公司的宏大规模,到高中生为大学录取作品集制作的“你好,我是 Jane”页面。从本地企业到电商巨头、政府机构、新闻网站,以及介于两者之间的所有领域,我们都看到了稳步的迁移。

但正如所有事物一样,好事也可能过犹不及。JavaScript 突破了 Web 体验所能达到的极限,但也带来了代价。那些没有最好的设备或最快的网络的人付出的代价最为惨重,但当事情没有按计划进行时,任何人都会感受到这种代价。

那些自诩为网络守护者的人对此非常关注。双方都对此表示关注。至此,应该清楚的是,实现一个放之四海而皆准的解决方案可能很困难,但肯定存在改进的空间。

共同点是减少向浏览器发送 JavaScript 的速度,最近,0kb 的 JS 框架就体现了这一点。但我想进一步阐述这一点,因为其影响远不止渐进式增强或惰性加载。一切都在朝着架构变革的方向发展,这种变革自十多年前 SPA 出现以来从未有过。

我们正在将路由放回服务器上。


多页应用程序(MPA)

图片描述

所以我们又回到了 PHP 和 Rails?不,我希望这不会让大家失望。每次我们都和上次不一样。但这并非一个糟糕的起点。大多数 Web 都只需要一个渲染 HTML 的网站。而且大多数 JavaScript 框架都允许你生成静态网站,或者至少在你的单页应用中生成一些静态页面,以保持低交互页面的快速和轻量。

但我们经历过这种情况,并且知道,对于所有AlpineJSStimulusPetite Vue来说,我们已经习惯了我们最喜欢的框架所带来的开发者体验优势,在第一个应用之上再开发一个应用远非理想之选。但对于大多数解决方案来说,要么全有,要么全无。无论<script>是否包含标签。除了最简单的需求之外,这都只是个小把戏,而不是一种体验。

相反,我们看到了在 2010 年代初期被称为“小部件”(widgets)但现在被称为“岛屿”(Islands)的领域出现了巨大的增长。这些独立的“岛屿”功能更强大,因为它们可以通过AstroSlinkityIles等最新工具进行服务器渲染和数据融合。这是一种粗粒度的方法,对许多网站来说效果很好,但我们也看到了该领域从头设计就考虑到这一点的更复杂的工具,例如MarkoQwik ,它们被应用于最大的电商解决方案中。

但无论在服务器端导航时如何操作,您都可以确定页面的某些部分永远不会在客户端渲染。这样可以大幅减少发送和执行的 JavaScript 代码。实际效果可能有所不同,但据报道,即使是像 eBay 的落地页,通过这种技术,代码大小也能减少 80% 到 90%。

然而,这并不是故事的结束,因为虽然完整的服务器重新加载对许多网站来说效果很好,但我们已经习惯了能够在 SPA 中保留客户端状态并进行更平滑过渡的好处。


HTML 框架

我还没找到这个方法的具体名称,但它被一些工具使用,最著名的是Turbo,它是 Rails 的 Hotwire 框架的一部分。但这种方法也适用于其他地方。本质上是拦截所有链接点击或表单提交并禁用默认行为,然后请求屏幕的新位置,并在完成后替换内容<body>

我们可以使用 MPA,让服务器处理路由,但在浏览器中导航时保留 JavaScript 应用的状态。每个面板加载时,我们会对其进行“hydrate”操作。由于我们知道它只能在服务器上渲染,因此上述所有优化方法均适用。

然而,现在我们需要 JavaScript 来协调这种转换。JavaScript 用量并不大。许多 MPA 框架如果支持延迟加载,无论如何都会加载一个小型引导加载程序,但在纯 MPA 中,可能不需要任何运行时。

虽然这种方法不那么繁琐,但仍然不够流畅。从服务器加载 HTML 并替换原有内容可能会保留应用状态,但 DOM 中不会有任何变化。没有焦点、动画、视频标签上的播放器位置等等……这就引出了下一个问题。


服务器组件

图片描述

答案竟然出自ReactReact 服务器组件的限制非常严格,几乎和孤岛的工作方式一模一样。你不能将服务器组件(“静态部分”)嵌套在客户端组件(“孤岛”)中,除非将其作为子组件传递。

实际上,这意味着服务器组件类似于 MPA,只不过你可以返回服务器,将页面的静态部分“重新渲染”为 VDOM,并让浏览器接收该 VDOM 并比较差异。尽管客户端组件得以保留,并且静态 HTML 中那些永远不会改变的部分不会被替换,但我们本质上讨论的是一种路由范式。

当您点击链接时,该链接会被拦截,服务器组件端点会处理该请求,并返回新的 VDOM 以供比较。当您执行修改操作以更新页面数据时,整个页面会在服务器上重新渲染,并返回新的 VDOM 表示。这很像您使用 MPA 进行的经典表单提交。

权衡利弊。嗯,每次服务器重新渲染时,都需要通过网络发送大量数据,但与 MPA 相比,实际数据量并不大。而且,与其他方法相比,MPA 需要更多的编排。你需要浏览器中的框架。因此,这种方法不一定能带来最快的页面加载速度。但它同样可以减少向浏览器发送大量不必要的组件代码。


分析

这是三种截然不同的解决方案,并非指其中一种取代了另一种。纯 MPA 有可能实现最佳的页面加载性能。HTML 框架是这三种方案中导航到新位置的最佳方案。只有服务器组件才有可能与我们如今的单页应用体验毫无二致。但这三种方案都采用相同的导航模型:全页面导航,并且来自服务器。

不仅仅是这些因素在推动我们这样做。考虑一下像Remix或 Sveltekit 这样提倡渐进式增强的框架。这自然会让你回归到表单回发和全页面导航。

接下来,考虑一下React Query之类的东西。现在越来越常见的做法是重新获取所有相关资源,而不是在发生突变时直接执行缓存更新。Remix乐观更新表单是另一个例子。它们使用路由结构在发生突变时刷新所有数据。

本质上,与其尝试将一堆昂贵的缓存逻辑引入浏览器,不如采取优先重新获取的心态。与重新加载整个页面进行渲染相比,这还不算太糟糕。这样做的好处是无需编写大量额外的客户端代码即可确保页面数据的一致性。你见过领先的 GraphQL 客户端的大小吗?gzip 压缩后大约 40kb。只需将它和 React 放在同一个页面上,无需编写任何代码,就能让你超越任何性能关键型网站的大小预算。

所有这些进展都指向同一件事。我们正回到服务器上的路由。


结论

鉴于此,我对未来有一些想法。我认为 MPA 作为一项技术,应该保持现状,并持续改进其能力,以实现更好的部分数据合并、更智能的延迟加载和更动态的交付(流式传输)。

我认为纯 HTML 框架是一个过渡阶段。随着服务器组件(尤其是非 VDOM 组件)新方法的出现,我们将看到它们被逐渐吸收。理想的做法是让服务器组件既能够提供细粒度的更新能力,又能够发送新渲染内容的 HTML。这样,初始页面加载或任何大型导航的 HTML 渲染速度都会更快。支持混合/部分格式或许是可行的。

不过,有趣的是,我们可以将从 SPA 中学到的技巧应用到这一点上。嵌套路由尤其值得一提,因为每个部分都是逻辑上的顶级入口点,在很多情况下可以独立更新。路由是 Web 上一切的支柱。

说实话,即使我们模糊了这些界限,很多事情仍然可能实现,而无需将所有内容都推到浏览器里。我们可以从简单的全页重新加载的MPA扩展到最复杂的应用程序。也许这些就是Rich Harris预测的#transitionalapps(过渡性应用)。但就我而言,只有一种方法可以找到答案。

让我们开始建造吧。

文章来源:https://dev.to/this-is-learning/the-return-of-server-side-routing-b05
PREV
使用 Visual Studio Code Dev Containers 整理你的机器
NEXT
JavaScript 悖论