服务端渲染概述

客户端渲染

在讲解服务端渲染之前,我们先回顾一下主流浏览器页面的渲染流程,步骤如下:

  1. 步骤01 浏览器通过请求得到一个 HTML 文本。

  2. 步骤02 渲染进程解析 HTML 文本,生成 DOM 树。

  3. 步骤03 解析 HTML 的同时,如果遇到内联样式或者样式脚本,则下载并生成 CSS 样式规则(Style Rules),若遇到 JavaScript 脚本,则下载执行该脚本。

  4. 步骤04 DOM 树和 CSS 样式规则树构建完成之后,渲染进程将两者合并成渲染树(Render Tree)。

  5. 步骤05 渲染进程开始对渲染树进行布局,生成布局树(Layout Tree)。

  6. 步骤06 渲染进程对布局树进行绘制,显示到页面中。

完整流程如图 10-1 所示。

image 2024 02 23 23 36 07 908
Figure 1. 图 10-1 浏览器渲染流程

浏览器请求到的这个 HTML 文件中加载了很多渲染页面需要的 JavaScript 脚本和 CSS 样式表,浏览器拿到 HTML 文件后,开始加载脚本和样式表,并且执行脚本,这个时候脚本请求后端服务提供的 API,并获取数据,获取完成后,将数据通过 JavaScript 脚本动态地渲染到页面中,完成页面显示。这就是客户端渲染的主要流程,如图 10-2 所示。

image 2024 02 23 23 37 05 243
Figure 2. 图10-2 客户端渲染的主要流程

前端团队接管了所有页面渲染的工作,后端团队只负责提供所有数据查询与处理的 API。

服务端渲染

服务端渲染的大体流程与客户端渲染有些相似,采用 Node.js 部署前端服务器。首先是浏览器请求 URL,前端服务器接收到 URL 请求之后,根据不同的 URL,前端服务器向后端服务器请求数据,请求完成后,前端服务器会组装一个携带了具体数据的 HTML 文本,并且返回给浏览器,浏览器得到 HTML 之后开始渲染页面,同时浏览器加载并执行 JavaScript 脚本,给页面上的元素绑定事件,让页面变得可交互,当用户与浏览器页面进行交互(如跳转到下一个页面)时,浏览器会执行 JavaScript 脚本,向后端服务器请求数据,获取完数据之后,再次执行 JavaScript 代码动态渲染页面,流程如图 10-3 所示。

image 2024 02 23 23 39 16 566
Figure 3. 图10-3 服务端渲染流程

这样用户在看到页面首屏主要内容时,只和服务器有一个 HTTP 请求交互,就是获取 HTML 页面内容,这个内容就是完整的页面内容。当然,后续的页面用户交互还是在前端进行的。

这样看下来服务端渲染要比客户端好很多,尤其是首屏的用户体验。以下从几个方面对服务端渲染和客户端渲染进行一个优劣对比:

  • SEO 支持:服务端渲染可以有效地进行 SEO,当爬虫工具请求用户的页面地址时,可以拿到完整的 HTML 内容,便于对网站内容进行收录;而客户端渲染爬虫工具拿到的只是一个空的 HTML 壳子,无法对网站内容进行完整收录。

  • 白屏时间:相对于客户端渲染,服务端渲染在浏览器请求 URL 之后已经得到了一个带有数据的 HTML 文本,浏览器只需要解析 HTML,直接构建 DOM 树就可以;而客户端渲染需要先得到一个空的 HTML 页面,这个时候页面已经进入白屏,之后还需要经过加载并执行 JavaScript、请求后端服务器获取数据、JavaScript 渲染页面几个过程才可以看到最后的页面,特别是在复杂的应用中,由于需要加载 JavaScript 脚本,越是复杂的应用,需要加载的 JavaScript 脚本就越多、越大,这会导致应用的首屏加载时间非常长,进而降低了用户体验。

  • 服务器运维:除了前端静态资源服务和后端接口服务外,服务端渲染还需要额外搭建一套 Node.js 服务,主要用来请求后端服务的数据和 HTML 组装,这在一定程度上提升了项目复杂度,同时需要更多地关注服务器的负载均衡及相关运维问题,同时由于代码需要,可以在服务端运行,也可以在浏览器端运行,需要兼顾两端的代码,提示了代码复杂度。

所以在使用服务端渲染之前,需要开发者考虑投入产出比,比如大部分应用系统都不需要 SEO,而且首屏时间并不是非常慢,如果使用服务端渲染,反而小题大做了。