服务端渲染原理
服务器端渲染和客户端渲染效果对比
为了更好的对比服务器端渲染和客户端渲染,开启低速模拟,来检验SSR对首屏加载速度的作用。
❗️以下示例均已关闭缓存,开启慢速3G模拟环境。

服务器端渲染结果
服务器端渲染网络不佳的情况下,仍然能保证进入页面就可以获取首页的文章数据,用户体验较好。
客户端渲染结果
客户端渲染网络不佳的情况下,会有长达半分钟的首页白屏,会造成用户体验不佳。
上面的对比可以得出结论,在网络不佳的情况下,SSR是加载速度优化的重要手段,毕竟现在动辄几兆乃至几十兆的JS文件,在极端情况下可能造成长达数十秒的白屏时间,而SSR就可以通过体积较小的HTML来优化用户体验,让用户先看到一部分结果。
❓那么为什么客户端渲染会出现长时间的白屏而浏览器渲染不会呢?
服务器端渲染的历史
早期服务器端渲染

早期服务器端渲染流程:
浏览器发起请求,用户访问页面 URL,向服务器端发送请求。
服务器端接收到请求后,开始处理页面请求。
服务器端查询数据库获取所需数据。
服务器端将获取到的数据填入后端模板(如 PHP、ASP、JSP 等),生成 HTML 片段。
服务器端将各个 HTML 片段组合成完整的 HTML 页面。
完整的 HTML 页面返回浏览器。
浏览器接收到 HTML 后直接解析并渲染显示页面
❓为什么现代服务端渲染优于早期服务端渲染?
早期服务器端渲染每次路由跳转(比如提交表单,切换页面等),都会重新向服务器发起请求,服务端返回新的完整 HTML,浏览器清空原有 DOM,重新解析、渲染,因此整个页面会闪一下(白屏/闪屏)。
现代服务器端渲染的页面切换是 前端框架(React/Vue)控制的局部更新,不需要清空 DOM。例如导航栏,侧边栏等元素一般不会重新刷新。
例如从
/home切到/about:传统 SSR:重新请求
/about.html→ DOM 清空重建 → 白屏。现代 SSR:React Router 拦截路由变化 → React 只更新必要的组件 → DOM 不被整体清空。
客户端渲染

客户端渲染流程如下:
浏览器请求 URL。
前端服务器端直接返回一个空的静态 HTML 文件,不需要查询数据库或做模板组装。HTML 文件中加载了渲染页面需要的 JavaScript 脚本和 CSS 样式表。
浏览器拿到 HTML 文件后开始加载脚本和样式表,并执行脚本。
脚本向后端服务请求 API 获取数据。后端服务器端查询数据库,获取数据并返回给js脚本。
获取到数据后,JavaScript 脚本将数据动态渲染到页面中,完成页面显示。
客户端渲染优点
可以向用户快速展示页面的内容,增加用户体验
给别人爬虫爬取相应的内容增加一定的困难
客户端渲染缺点
可能需要向服务器端请求多次数据
不利于SEO搜索引擎优化,即百度等搜索引擎搜索不到客户端渲染的数据
存在白屏
服务器端渲染

服务器端渲染流程如下:
浏览器请求 URL,向前端服务器端发起请求。
前端服务器端根据不同的 URL,向后端服务器端请求数据。
前端服务器端获取数据后,生成包含具体数据的 HTML 文本,并返回给浏览器。
浏览器接收到 HTML,开始渲染页面内容。
浏览器加载并执行 JavaScript 脚本,为页面元素绑定事件,使页面可交互,该过程也被称为水合。
当用户在页面中交互(如跳转到下一个页面)时,浏览器执行 JavaScript 脚本并向后端服务器端请求数据。
浏览器获取到新数据后,再次执行 JavaScript 动态更新页面,而不是重新加载整个页面。
服务器端渲染的利弊
SSR 的优点
更快的初始加载:首屏加载快,服务器端向客户端发送已完全渲染的页面,因此用户可以更快地查看内容。
改进的 SEO:由于内容在 HTML 负载的第一个字节中存在,搜索引擎能够更轻松地抓取和索引内容。
更好的低性能设备表现:由于大部分工作在服务器端上完成,几乎任何低性能的设备都能在短时间内绘制页面。
SSR 的缺点
服务器端压力大:在 SSR 中,服务器端需要为每个用户请求生成完整的 HTML 页面,当并发请求量较高时,服务器端的 CPU 和内存资源消耗会显著增加,可能导致服务器端响应变慢甚至出现性能瓶颈,影响用户体验。
开发维护复杂:由于需要在服务器端端处理页面渲染逻辑,涉及到服务器端端的技术栈和框架,增加了开发的复杂性。开发人员需要同时掌握前端和后端的知识,并且在出现问题时,调试和排查错误也相对困难,提高了开发和维护成本。
首屏加载优化成本高:虽然 SSR 理论上能提高首屏加载速度,但如果页面中包含大量的动态数据和复杂的交互逻辑,服务器端端生成页面的时间可能会变长,反而影响首屏加载性能。而且为了实现更好的首屏加载体验,可能需要进行更多的优化工作,如代码拆分、缓存策略等,增加了开发的工作量。
不利于跨平台:SSR 通常是基于特定的服务器端端技术和环境进行开发的,在跨平台和跨环境部署时可能会遇到兼容性问题,需要进行额外的适配工作。
❓为什么客户端渲染长时间白屏,而服务器端渲染不会
客户端渲染会白屏的原因
初始 HTML 是空壳
浏览器一开始拿到的 DOM 里面,#root 是空的。浏览器必须先下载bundle.js,解析bundle.js,执行React 的render()才能生成 DOM。如果bundle.js体积大、网络慢、JS 运行耗时,就会导致用户长时间看到白屏。页面不仅要渲染 DOM,还要绑定事件。在 CSR 里,必须等 JS 完全跑完,用户才能交互。
服务器端渲染不会白屏的原因
服务端渲染的HTML 一开始就带内容,SSR 返回的 HTML 已经是完整 DOM:
浏览器解析 HTML 的同时,直接把内容绘制出来 → 用户立刻看到页面。
JS 异步加载,即使 bundle.js 还没下载完,用户也能先看到“静态页面”。页面交互功能会在水合后补上,但用户不会面对空白屏幕。
混合渲染实例
本实例编写了一个博客网站,可以选择首页使用SSR渲染也可以选择首页使用CSR渲染,登陆及新增文章操作操作通过 CSR 完成。
服务器端渲染泳道图
.png)
客户端渲染泳道图
.png)
服务器端渲染的实现
服务器端渲染通常分为两个阶段:
阶段一(服务端渲染阶段):在服务端生成 HTML → 提供快速的首屏渲染。
阶段二(客户端水合阶段):在浏览器加载 JS → 让页面变成可交互应用。
服务器端主要步骤
脱水实现
概念:将组件的状态(state/props)序列化为 JSON,随 HTML 一起发送到客户端。
目的:
客户端在注水时可以复用状态,不必重新请求数据
提升首屏渲染性能
流程:
服务器端渲染组件 → 生成 HTML
获取组件内部状态(如 React state 或 Vue store)
序列化状态为 JSON → 嵌入 HTML(通常放在
<script>标签里)
实现代码
步骤1:首先数据获取与格式化
步骤2:React组件渲染
步骤3:数据序列化和注入
此步骤会将序列化后的数据直接嵌入到HTML的script标签中
客户端主要步骤
注水实现
概念:客户端接收 SSR HTML 后,将其转换为可交互的组件。
目的:
激活事件绑定(如点击、输入)
恢复组件状态(从脱水 JSON 中读取)
保持与服务器端渲染一致的 UI
流程:
客户端 JS 解析 HTML
读取脱水的 JSON 状态
绑定事件监听器和生命周期函数
页面变为可交互
实现代码
步骤1:从服务器端注入的全局变量中恢复数据
步骤2:获取DOM容器
步骤3:创建React应用实例
步骤4:使用hydrateRoot进行水合
React水合的详细步骤
调用
hydrateRoot()hydrateRoot()初始化 React 根节点创建一个 Fiber Root(Fiber 树的根节点),关联已有 DOM
构建 Fiber 树
React 根据 JSX 构建虚拟 DOM
创建 Fiber 节点:
Fiber 节点和服务器端 HTML DOM 节点一一对应,形成 Fiber 树
对比已有 DOM(Reconciliation)
React 会把新建的 Fiber 树与现有 DOM 对比
复用 DOM:
节点类型一致 → 复用原 DOM
属性/内容不同 → 更新 DOM
节点类型不同 → 删除旧 DOM,创建新 DOM
避免整个页面重新渲染,提高性能
绑定事件
React 为复用的 DOM 节点 挂载事件处理函数
例如
onClick,onChange等原来静态的 HTML 此时变成可交互的 React 元素
执行生命周期和副作用
类组件:
- 调用
componentDidMount
- 调用
函数组件:
- 执行
useEffect中的副作用
- 执行
页面状态与 DOM 完全同步
激活更新机制
Fiber 树启动 调度机制
后续状态更新(setState / useState)都会走 Fiber 的增量渲染
页面从静态 HTML 变成真正的 SPA
