发布于 2026-01-05 4 阅读
0

JavaScript 中的服务器端渲染:为什么要使用 SSR?为什么要使用服务器端渲染?别再追求瀑布式开发了!人人都能使用的现代工具!下一步是什么?DEV 全球展示挑战赛,由 Mux 呈现:展示你的项目!

JavaScript 中的服务器端渲染:为什么选择 SSR?

为什么选择服务器端渲染?

不要追逐瀑布

人人适用的现代工具

接下来是什么?

由 Mux 主办的 DEV 全球展示挑战赛:展示你的项目!

服务器端渲染(Server-Sender)是目前 JavaScript 框架领域的热门话题。例如,Vercel 的 Next.js 就获得了 4000 万美元的新融资,成为新闻热点。Next、Nuxt、Gatsby 和 Sapper 等框架在过去几年里都非常流行,而 JAMStack 的兴起也推动了静态站点生成(Static Site Generation)技术的应用。

但你或许更应该关注的是,框架本身在过去两年里一直在大力投入这一领域。我们一直在等待 React 的 Suspense 功能,或者经常看到关于Island 架构的博客文章,SvelteVue也一直在将元框架类型的项目纳入其核心框架之下,这都是有原因的。这才是大家都在追求的目标。

所以今天我想花点时间来填补空白,谈谈底层技术,并总体上更好地描绘出正在发生的事情。

为什么选择服务器端渲染?

为什么要使用服务器端渲染?对某些人来说,这可能显而易见。但对我来说并非如此。

我的意思是,有很多方法可以降低 JavaScript 的初始性能开销。我甚至曾立志要向人们证明,一个经过精心调优的纯客户端单页应用 (SPA) 在几乎所有指标(甚至包括首次绘制时间)上都能胜过典型的服务器端渲染 SPA。而且现在爬虫程序也能抓取动态 JavaScript 页面进行 SEO 优化了。那么,这一切的意义何在呢?

即使现在的爬虫完全能够抓取这些大量使用 JavaScript 的网站,它们仍然会被降级到第二层级,导致索引时间更长。这或许并非所有人都介意,但确实需要考虑。此外,页面上渲染的元标签通常用于社交分享链接。这些爬虫往往不够智能,因此只能抓取到初始存在的标签,这些标签在每个页面上都相同,从而无法提供更具体的内容。

但这些并非新问题。所以,让我们来看看我认为引发当前讨论的更重要的驱动因素。

不要追逐瀑布

JavaScript 包的大小一直在增长,而且还在持续增长。并非所有网络连接速度都相同。在网络速度较慢的情况下,服务器端渲染 (SSR) 能在初始加载时更快地向用户显示内容。因此,如果您需要绝对最快的页面加载速度,那么 SSR 无疑是最佳选择。

归根结底,浏览器只有在收到HTML页面后才会开始执行其他操作。只有在开始接收HTML之后,才会请求其他资源。

对于动态客户端 JavaScript 页面(例如 SPA)甚至是静态生成网站的动态部分(例如使用 Gatsby 或 Next 创建的网站),通常这意味着在页面稳定之前至少需要 3 次级联往返。

替代文字

需要注意的是,这不仅仅是网络瓶颈。从解析各种资源到执行 JavaScript 代码发出异步数据请求,所有环节都处于关键路径上,无法并行处理。

问题就在这里。而保持包体积小的愿望更是加剧了这个问题。代码分割功能强大,而且在路由边界上很容易实现,但简单的实现最终会变成这样:

替代文字

连续四次往返!主程序包在执行之前不知道要请求哪个页面块,而加载并执行该页面块之后,它才能知道要请求哪些异步数据。

服务器端渲染是如何解决这个问题的?

了解当前路由可以让服务器即使在代码拆分的情况下也能将所需的资源直接渲染到页面中。您可以添加<link rel="modulepreload" />标签或标头,以便在初始包解析和执行之前就开始加载模块。

此外,它还能在服务器收到请求后立即开始异步数据加载,并将数据序列化回页面。因此,虽然我们无法完全消除浏览器瀑布效应,但可以将其减少到一次。然而,这种简单粗暴的方法实际上会延迟 HTML 页面的初始响应。所以,这并非一次彻底的胜利。

替代文字

事实上,我们还可以做更多的事情,我将在后续文章中详细介绍。

初始负载后

首次加载后,情况就完全不同了。资源可以通过 Service Worker 进行预加载/缓存。JavaScript 甚至以字节码的形式存储,因此无需解析。除了异步数据请求之外,所有内容都是静态的,可以直接存在于浏览器中。没有瀑布式渲染,这甚至比服务器端渲染的最佳情况还要好。

替代文字

但使过时的服务工作线程和缓存资源失效则完全是另一回事。对于某些类型的应用程序来说,在重新验证期间保持数据更新可能大有裨益。而那些需要保持数据最新的网站可能不会选择这种方式,而是使用他们可以更好地控制的缓存。

因此,关于性能/大小这个话题,关键在于客户端本身有很多方法可以缓解除首次加载新内容之外的大部分问题。首次加载新内容的速度始终会受到网络速度的限制。但是,随着应用程序规模的扩大,如果不加以充分考虑,SPA 的性能很容易下降,而简单地应用最佳实践只会引入其他潜在的性能瓶颈。

如果初始负载对我们的网站和应用程序至关重要,那么服务器端渲染可以减轻一些重要的负载。

人人适用的现代工具

我们需要退后一步,从更宏观的角度来看待这个问题。网站的数量远远多于Web应用程序。这种情况一直存在,但人们对现代JavaScript框架的看法已经发生了变化。

客户端 JavaScript 框架最初开发时,目标很简单:找到一种方法,在浏览器中完成所有不必要的服务器端请求。当时我们构建的用户界面越来越复杂,而用户逐渐习惯了原生应用体验,因此页面全屏刷新是不可接受的。

这些工具的开发可能最初是面向交互式 Web 应用程序,但还有一大批潜在用户正在积极寻找这些框架来搭建更简单的网站。

这是一个非常引人深思的问题。尤其考虑到客户端和服务器之间的协调如果手动高效完成会非常复杂。任何超出其原始参数范围的使用都需要特别考虑。

JS框架与服务器框架

这种难题并非 JavaScript 框架独有。在 Rails 或任何传统后端渲染的项目中,添加大量动态 JavaScript 代码都会面临同样的复杂性。只不过,JavaScript 框架将此视为创造完全同构体验的独特契机。在这种体验中,只需一套代码库即可构建整个网站。这有点像过去,但又截然不同。

客户端库解决的根本问题在于状态管理。这正是 MVC 架构不适合客户端的根本原因。状态需要有人维护。MVC 及其单例控制器非常适合 RESTful API 等无状态应用,但需要特殊的机制来处理非模型数据的持久化。有状态客户端和无状态服务器意味着页面刷新是不可接受的。

服务器框架面临的挑战在于,即使有了像Hotwire这样的局部更新机制,也无法简化客户端的复杂性。你可以忽略客户端的存在,如果你的需求不高,这或许就足够了。否则,你最终还是要做很多相同的工作。这实际上就相当于维护两个应用程序。

这就是为什么 JavaScript 框架具有独特的优势,能够提供这种统一的用户体验。这也是为什么它对框架开发者如此具有吸引力。

接下来是什么?

做好准备,以后你会经常听到这个消息。这件事已经持续了大约两年,但这些项目终于开始发展到人们可以放心谈论的程度。这需要时间,因为这是一个根本性的转变。虽然有像 Next 和 Nuxt 这样的框架,但它们的核心库并没有针对这些情况进行优化。

除了 eBay 的Marko之外,我们至今还没有看到这类解决方案应有的成熟度。但这种情况正在改变。React服务器组件就是一个例子。Vue、Preact、Svelte 等等,它们也都在这个领域开发各自的解决方案。

JavaScript 服务器端渲染是这些框架的下一个主要发展方向。但最终是否使用它,仍然取决于你。

文章来源:https://dev.to/ryansolid/server-rendering-in-javascript-why-ssr-3i94