前言
这篇文章为服务端渲染 SSR 概念篇,可以帮助你快速了解服务端渲染的相关知识。 实战篇可以看(强烈推荐) 👉【超详细】React SSR 服务端渲染实战
一、SSR的发展
1.1 客户端渲染
页面初始加载的 HTML 没有内容,等到 JavaScript 文件加载完毕后才渲染页面内容,同时完成交互事件的绑定。
架构图
优点
- 局部更新
- 交互效果丰富
- 前后端构建分离
- 前后端职责划分清晰
缺点
- SEO 不友好
- 首屏渲染时间长
1.2 传统服务端渲染
由服务端使用模板引擎将模板与数据拼接成完整的html返回给浏览器,又称后端模版渲染(jsp 或 php)。
优点:
- 减少客户端白屏时间
- 对 SEO 友好
缺点:
- 页面跳转体验差,
- 前后端职责不清楚、项目难以维护
1.3 现代服务端渲染
现代服务端渲染结合了React、Vue 这类框架,可将传统服务端渲染和客户端渲染的优点结合起来,既能降低首屏耗时, 又能有 SPA 的开发体验。这种渲染方式又可以称为”同构渲染”。那什么是 “同构渲染”呢?
同构渲染: 将内容的展示和交互写成一套代码,这一套代码运行两次,一次在服务端运行,来实现服务端渲染,让 html 页面具有内容,另一次在客户端运行,用于客户端绑定交互事件。
架构图
优点
- 降低首屏耗时
- 对搜索引擎友好
缺点
- 服务端压力增加,需要将完整的页面返回给客户端
- 项目复杂度增加,维护成本高,需要做一些代码兼容
二、同构渲染的必要条件
是什么因素让同构渲染成为了可能?同构渲染之所以能实现离不开两点:Node.js & 虚拟 DOM。
2.1 Node 环境
Node 环境支持 JavaScript 的运行,才能使得同构渲染的项目能运行在服务端。
Node 和 浏览器都是 JavaScript 的运行环境,但是两者的侧重不同。浏览器中的 js 更多是操作 DOM,而 Node 中的 js 用途是操作磁盘或者搭建服务器等。两者的不同也导致了同构代码在不同环境下有一定的差异。
2.2 虚拟 DOM
仅仅有 Node 的存在是不够的,如果同构代码中存在直接操作 DOM 的代码,在 Node 中运行肯定会报错。而像 React、Vue 这些框架都有虚拟 DOM 的概念,它们不直接操作 DOM,而是操作虚拟 DOM,虚拟 DOM 实际上是一个 js 对象,这样就使得同构渲染成为可能。
三、同构渲染的原理
基于现代框架提供的虚拟 DOM,服务端可以在 node 环境中生成虚拟 DOM,再把虚拟 DOM 转化为串,当作 HTML 的内容输出。
服务端脱水:一个组件的结构可理解为 HTML + 数据 + 交互 组成。由服务端只会将组件的 HTML 结构部分提取出来,通过 renderToString
等方法渲染的出来的只有 HTML 的DOM 结构,没有数据&交互。
客户端注水:复用服务端渲染的DOM结构,将交互绑定到对应的HTML上,使其可交互。
四、同构流程
4.1 核心步骤
- 用户请求 & 服务端处理
- 用户输入URL: 浏览器向服务器发送页面请求
- 服务器匹配路由: 服务器根据 path,匹配前端路由
- 数据预取: 服务器调用组件的 loadData 方法,请求组件的数据
- 服务端渲染
- 框架将组件树渲染为完整的HTML字符串(
renderToString
或renderToPipeableStream
) - 将预取的数据嵌入 HTML,供客户端使用(比如通过
<script>
标签保存为window.__INITIAL_STATE__
)
- 框架将组件树渲染为完整的HTML字符串(
- 返回 HTML 和资源
- 服务端将 HTML 返回给客户端
- 客户端渲染 HTML 的内容
- 客户端水合
- 浏览器加载执行 js
- 客户端基于组件代码和初始化数据
window.__INITIAL_STATE__
,重新生成虚拟DOM - 客户端将虚拟 DOM 和服务端 HTML 进行对比(
ReactDOM.hydrateRoot
),相同则复用DOM,并绑定事件交互。
4.2 流程总结
用户请求 → 服务端生成HTML字符串 → 发送至浏览器 → 浏览器显示并加载 js 文件 —> js 执行 & 页面可操作