设计网站作为单页应用(SPA)似乎很流行。SPA不会在每次单击时显示新的HTML页面,而是发送带有JavaScript的轻量级骨架。JS发出HTTP请求,接收JSON,并将数据注入DOM。在每次用户操作时,页面不会重新加载—只有DOM发生变化。这样的架构,曾经是对慢速浏览器和不可靠网络的响应,现在成为瓶颈。页面由各种片段构建,每个片段都需要自己的HTTP请求。无论每个请求有多快,乘法都会减少所有优化努力。
在2000年,Roy Fielding,REST的作者和HTTP的合著者,在他的论文中描述了Web:
简而言之,每个动作导致一个HTML页面重新加载。
1999年,微软推出了XMLHttpRequest用于Outlook Web Access,实现了后台HTTP请求。2005年,Jesse James Garrett在他的文章《Ajax: A New Approach to Web Applications》中创造了AJAX这个术语。当完整页面重新加载效果不理想时,这变得非常有用。
网络不可靠
全页面重新加载感觉很浪费
用户期望“类似桌面”的交互性。
AJAX 为 SPA 铺平了道路:服务器发送 JSON,浏览器在本地重建页面,而不是 HTML。然后出现了框架:Angular、React、Vue。每个框架都形式化了同样的想法:浏览器托管应用程序,服务器提供数据。
想象一个简单的在线计算器,用户输入”2+3”,点击”提交”,就能在输入框旁边看到结果。无需重新加载页面。DOM 保持不变。浏览器中的 JS 只改变计算结果,放在一个 <div/> 中。
这种架构的主要理由是 性能。如果服务器慢,渲染完整的 HTML 页面比只发送包含计算结果的 JSON 要慢。如果网络不稳定,重新发送整个 HTML 比只发送小型 JSON 文档要慢。此外,如果浏览器慢,对 DOM 进行微小更改比重新加载整个页面更快。
对于一个简单的例子来说这是正确的。然而,当 SPA 变得更大时,前端必须向后端进行 数十次往返。看看 Facebook 和 LinkedIn 在渲染主页时所做的工作。一个非常简单的 UI,只是最近帖子的列表,却需要多个部分填充,每个部分都需要发起自己的 HTTP 请求,有时需要几秒钟以上才能完成页面渲染。如果你问我,他们的用户体验很糟糕。
他们的架构师愚蠢吗?不是的。
SPA 这个想法本身就有缺陷。Facebook 和 LinkedIn 的架构师受困于这个想法。他们无法让网站运行更快,因为它们设计上是由后端检索的片段构建的。它们必须进行 多次 HTTP 往返。
SPA 的性能惩罚是结构性的,而不是偶然的。即使 HTTP/2 多路复用 请求,UI 仍然需要等待 JSON 到达才能继续进行—经典的 头部阻塞 在应用层。更糟糕的是,一个请求通常会透露出下一个请求所需的权限、功能标志或实体标识符,使并行调用变成了一个瀑布。缓存也无济于事:数十个端点,每个都有自己的 TTL,很少产生完全缓存命中,部分命中仍然迫使浏览器在运行时组装和协调页面。同时,浏览器必须在有意义的内容变得可见之前处理布局稳定、加载指示器和部分失败。
在慢浏览器和不可靠网络的时代,曾经是小型 DOM 更新的解决方案,现在已经成为 Web 设计的死胡同。
大多数 Web 架构师简直无法让他们的网站像 Stack Overflow 那样快,后者并非 SPA。它通过 Razor 在服务器上渲染整个 HTML 页面,在一次 <50ms 请求中完成。它确实选择性地使用客户端 JS 组件,但这些组件是独立的,不会抵消服务器 HTML 在初始体验中的核心作用。如果你问我,它们的用户体验是现代 Web 中最好的之一。
在服务器上渲染完整页面可能仍然是一个缓慢的操作。它可能会,而且通常会。但是,这个问题是 可以解决的,例如通过缓存的帮助。服务器负责数据和导航状态,从而实现缓存。
就我所知,每个大型、内容丰富、面向消费者的 SPA 在用户体验方面都很糟糕。甚至 Gmail 也不例外。如果它们遵循 Roy Fielding 的原则并在每次打开邮件时重新加载页面,它们的用户体验会明显好很多。我不是在开玩笑。
Translated by ChatGPT gpt-3.5-turbo/42 on 2026-01-25 at 15:19
