什么是SSR?它解决了什么问题?怎么做SSR?⭐️⭐️ 实操
SSR(Server-Side Rendering,服务端渲染 )是一种 将Html内容在服务器端生成并返回给客户端 的技术,与传统的客户端渲染(CSR)相比,它能显著 优化首屏加载速度 、 SEO(搜索引擎优化)。
一、核心概念:什么是 SSR?
在 客户端渲染(CSR) 中,浏览器从服务器获取的是几乎空白的 HTML (仅包含 <div id="app"></div> 等容器 ),然后通过加载 JavaScript 脚本,在客户端动态生成 DOM 并渲染页面(典型如 Vue、React 单页应用)。
首屏渲染 :SPA(Single Page Application)将页面渲染放到客户端执行,并且在渲染之前要加载大量的Javascript代码,所以首屏渲染需要花费较长时间。而SSR直接在服务端渲染完再返回客户端,使用户能够快速看到页面内容。
而 SSR 则是在 服务器端 提前将 Vue、React 等框架的组件渲染成完整的 HTML 字符串,包含页面的所有内容 ,然后将这个完整的 HTML 直接返回给浏览器 。浏览器收到后无需等待 JavaScript 加载完成即可展示页面,后续再通过客户端脚本 "激活"(hydrate)页面,使其具备交互能力。
二、工作流程:SSR 如何运作?
以 Vue/React 框架的 SSR 为例,核心流程如下:
- 客户端发起请求 :用户访问某个 URL(如
https://example.com/page)。 - 服务器接收请求:Node.js 服务器(如 Express、Koa)接收请求,解析路由。
- 数据预获取:服务器根据路由,提前请求所需数据(如接口调用、数据库查询)。
- 组件渲染 :将数据注入到 Vue/React 组件中,在服务器端通过框架的 SSR 引擎(如
vue-server-renderer、ReactDOMServer)将组件渲染为完整的 HTML 字符串。 - 返回完整 HTML :服务器将渲染好的 HTML 字符串(包含
<html>、<head>、<body>及所有内容)返回给客户端。 - 客户端 "激活" :浏览器展示 HTML 内容(首屏快速可见),同时加载客户端 JavaScript 脚本,脚本执行后 "激活" 页面(绑定事件、初始化状态),使页面从静态变为可交互的动态页面。
三、SSR 与 CSR 的关键区别
| 特性 | 客户端渲染(CSR) | 服务端渲染(SSR) |
|---|---|---|
| 首屏加载速度 | 慢(需加载 JS 后动态渲染) | 快(直接返回完整 HTML,浏览器可立即解析) |
| SEO 友好性 | 差(搜索引擎可能,无法解析动态生成的内容) |
好(搜索引擎可直接抓取完整 HTML 内容) |
| 服务器压力 | 小(仅提供静态资源和接口) | 大(需处理渲染和数据请求,需更高性能服务器) |
四、SSR 的优势
- 优化首屏加载速度 :客户端无需等待 JavaScript 下载、解析和执行,直接展示服务器返回的完整 HTML ,尤其对网络条件差或设备性能弱的用户更友好。用户能更快看到页面内容,减少 "白屏时间",降低跳出率。
- 提升 SEO 效果 :搜索引擎爬虫更易抓取服务器返回的静态 HTML 内容(传统 CSR 中,爬虫可能只看到空白容器,导致页面内容无法被索引)。
五、SSR 的劣势
-
增加服务器负载 :服务器需要处理渲染逻辑和数据请求,相比仅提供静态资源的 CSR 服务器,CPU 和内存消耗更高,需要更强的服务器性能或负载均衡策略。
-
开发复杂度提高:
- 代码需兼容: "服务端渲染 " 和 "客户端激活" 两个环境
- 需处理数据同步问题:(服务端预获取的数据需传递给客户端,避免客户端重新请求)。
- 构建和部署流程更复杂(
需 Node.js 服务器环境,无法直接部署到纯静态文件服务器)。
-
部分浏览器 API 限制 :服务端渲染时无法访问浏览器特有的 API(如
window、localStorage),需在客户端激活后使用。
六、适用场景
- 对首屏加载速度和 SEO 要求高的网站 (如电商首页、博客、新闻网站)。
不适合的场景:
- 后台管理系统,用户对首屏速度不敏感,SEO 无关紧要。
七、实现方式:如何落地 SSR?
1. 基于框架的原生方案
- Vue 2/3 :使用官方的
vue-server-renderer(Vue 2 )或@vue/server-renderer(Vue 3),配合 Node.js 服务器(如 Express)实现。 - React :使用
ReactDOMServer进行服务端渲染,ReactDOM.hydrate进行客户端激活,常见搭配 Next.js 框架简化开发。
2. 成熟的 SSR 框架(降低开发成本)
- Next.js(React 生态):最流行的 SSR 框架之一,内置 SSR、静态生成(SSG)、增量静态再生(ISR)等功能,简化路由、数据预获取等逻辑。
- Nuxt.js (Vue 生态):类似 Next.js,为 Vue 提供 SSR 解决方案,支持自动路由、数据预获取(
asyncData、fetch方法)等。 - SvelteKit(Svelte 生态):支持 SSR 和静态生成,以轻量高效为特点。
3. 核心实现要点(以 Vue + Express 为例)
- 服务端配置 :通过
vue-server-renderer创建渲染器(createRenderer),将组件渲染为 HTML。 - 数据预获取 :在服务端路由中提前获取数据(如
asyncData方法),并注入到组件的context中。 - 客户端激活 :客户端引入同一份组件代码,通过
hydrate方法激活页面,复用服务端渲染的 DOM。 - 避免浏览器 API :服务端渲染时,需通过
process.env.VUE_ENV区分环境,避免在服务端调用window等 API。
总结
SSR 通过服务端提前渲染 HTML 解决了 CSR 的首屏速度和 SEO 问题,但其代价是更高的服务器负载和开发复杂度。实际开发中,需根据业务需求(如 SEO 重要性、首屏速度要求)选择是否使用,或结合 SSG、CSR 混合使用(如 Next.js 的 "混合渲染" 模式)。对于 React/Vue 开发者,推荐直接使用 Next.js/Nuxt.js 等成熟框架,降低 SSR 落地难度。
Vue 服务端渲染框架(SSR)
Nuxt.js 和 vue-server-renderer 均服务于 Vue 生态的服务端渲染(SSR),核心关系是 "Nuxt.js 基于 vue-server-renderer 封装,是更高层的框架"。
vue-server-renderer 是 Vue 官方提供的底层工具库,核心功能是将 Vue 实例(组件树)渲染为 HTML 字符串,是 Vue 实现 SSR 的 "基石"。
Nuxt.js 作为 Vue 生态的 SSR 框架,本质是对 vue-server-renderer 的封装 :它在底层调用 vue-server-renderer 的 createBundleRenderer 等 API 完成服务端渲染的核心逻辑(将 Vue 组件转换为 HTML),但在此之上增加了大量工程化能力(如自动路由、数据预取、构建配置等),简化了开发者的使用成本。
-
选择建议:
- 若需高度定制 SSR 逻辑(如复杂的缓存策略、中间层架构 ),用
vue-server-renderer手动搭建; - 若追求开发效率,快速实现 Vue SSR/SSG 应用,优先用 Nuxt.js。
- 若需高度定制 SSR 逻辑(如复杂的缓存策略、中间层架构 ),用
以下是使用 Vue 实现 SSR 的基本步骤:
-
安装相关依赖:确保你的项目中已经安装了 Vue、Vue Router、Vue Server Renderer 等依赖。
-
创建服务器入口文件 :创建一个服务器入口文件,如
server.js。在文件中引入必要的模块,包括 Vue、Vue Server Renderer、Express(或其他后端框架)等。 -
编写服务器端渲染逻辑:
- 创建一个 Vue 实例,配置路由、数据等。
- 使用 Vue Server Renderer 的
createRenderer方法创建一个 renderer 实例。 - 在路由处理器中调用 renderer 实例的
renderToString方法来将 Vue 实例渲染为字符串。
-
处理静态资源:在服务器端渲染时,需要处理静态资源的加载和引用。可以使用 Webpack 进行服务器端渲染的配置,以处理静态资源的导出和加载。
-
客户端激活 :在服务器端渲染后,需要在客户端激活 Vue 实例,以便能够响应交互事件和更新页面。可以在 HTML 中插入一个 JavaScript 脚本,并在脚本中使用
createApp方法来创建客户端应用程序实例。
在 Vue2项目中 Nuxt.js 框架实现SSR
在 Vue2 项目中用 Nuxt.js 实现 SSR 非常简单,因为 Nuxt.js 2 本身就是基于 Vue2 设计的 SSR 框架,能直接复用 Vue2 的语法和组件。以下是针对已有 Vue2 项目的改造步骤(如果是新项目,直接按步骤 1 创建即可),核心逻辑是 "用 Nuxt2 的约定式配置替代 Vue2 的手动配置"。
一、如果是新建项目(最简单,推荐)
直接创建基于 Vue2 的 Nuxt2 项目,自带 SSR 能力:
1. 创建 Nuxt2 项目(确保基于 Vue2)
bash
# 安装 Nuxt2 脚手架
npm install -g create-nuxt-app
# 创建项目(命名为 vue2-nuxt-ssr)
create-nuxt-app vue2-nuxt-ssr
关键配置选择(其他默认):
Choose Vue version→ 选2.x(绑定 Vue2);Choose rendering mode→ 选Universal (SSR / SSG)(开启 SSR);
2. 写一个 SSR 页面(用 Vue2 语法)
Nuxt2 会自动将 pages 目录下的 .vue 文件转为路由,在 pages/index.vue 中写页面(完全兼容 Vue2 选项式 API):
html
<!-- pages/index.vue -->
<template>
<div>
<h1>Vue2 + Nuxt2 SSR 示例</h1>
<!-- 服务端渲染的动态数据 -->
<p>服务端生成时间:{{ serverTime }}</p>
<p>服务端随机数:{{ randomNum }}</p>
</div>
</template>
<script>
// 完全遵循 Vue2 选项式 API
export default {
// asyncData:Nuxt2 服务端数据预取方法(Vue2 项目中核心)
// 作用:在服务端渲染前执行,返回的数据直接注入页面 HTML
asyncData() {
return {
// 服务端获取当前时间(服务端无 window,直接用 Date)
serverTime: new Date().toLocaleString(),
// 服务端生成随机数(每次刷新由服务端重新计算)
randomNum: Math.random().toFixed(4)
}
}
}
</script>
3. 运行并验证 SSR 生效
bash
cd vue2-nuxt-ssr
npm run dev # 启动服务(默认 http://localhost:3000)
验证 SSR :访问页面后,右键 → "查看页面源代码",如果能在源码中直接看到 服务端生成时间:xxx 和 服务端随机数:xxx(而非空标签),说明 SSR 成功(服务端已将数据渲染到 HTML 中)。
二、如果是改造已有 Vue2 项目(核心步骤)
如果已有一个 Vue2 项目(如 Vue-CLI 创建),只需 3 步改造为 Nuxt2 SSR 项目:
1. 安装 Nuxt2 依赖
js
# 进入现有 Vue2 项目
cd your-vue2-project
# 安装 Nuxt2 核心依赖(依赖 Vue2)
npm install nuxt@2.x --save
2. 调整目录结构(按 Nuxt 约定)
Nuxt2 用约定式目录替代手动配置,需添加 / 修改:
plaintext
your-vue2-project/
├── pages/ # 新增:路由页面(Nuxt 自动生成路由)
│ └── index.vue # 首页(内容同上一步的 index.vue)
├── package.json # 修改启动脚本
└── nuxt.config.js # 新增:Nuxt 配置文件(极简版)
-
nuxt.config.js(极简配置):
javascriptmodule.exports = { mode: 'universal' // 关键:开启 SSR 模式 } -
package.json(添加启动脚本):
json"scripts": { "dev": "nuxt dev" // 启动 Nuxt 开发服务 }
3. 迁移核心代码
- 将原 Vue2 项目的
src/App.vue核心内容迁移到pages/index.vue; - 原路由(
src/router)无需保留,Nuxt 会根据pages目录自动生成路由; - 原组件(
src/components)可直接复用(复制到项目根目录的components文件夹)。
4. 运行验证
bash
npm run dev
访问 http://localhost:3000,查看页面源码验证 SSR 生效(同新建项目步骤)。
核心逻辑总结
Vue2 + Nuxt2 实现 SSR 的本质是:
- 用 Nuxt2 的
universal模式开启服务端渲染; - 通过
asyncData方法在服务端预取数据(替代 Vue2 客户端请求数据的逻辑); - 依赖 Nuxt2 自动处理 "服务端渲染 HTML + 客户端激活 " 的全流程(无需手动配置
vue-server-renderer或 Webpack)。
核心验证方式:页面源码中能看到动态数据(服务端已渲染),而非客户端 JS 动态插入的空容器。验证方法 :blog.csdn.net/csdn_girl/a...
面试题
Vue SSR 的工作流程是什么
- 在服务器端运行 Vue 实例,根据路由生成 HTML。
- HTML 包含渲染好的内容和状态,并发送到客户端。
- 客户端接管页面,进行 "激活"(hydration),绑定交互逻辑。
为什么需要将 Vue 的组件和状态序列化到 HTML 中
在 SSR 中,Vue 的状态(如 Vuex 的 state)需要通过 script 标签嵌入到 HTML 中,供客户端使用
客户端和服务器端的应用实例有何不同?
- 服务端实例专注于生成 HTML,不能直接操作 DOM
- 客户端实例负责挂载到已有的 DOM 上,并补充交互逻辑
服务端渲染需要哪些基本配置?
如何处理路由和数据预取?
- 路由:基于 Vue Router,确保客户端和服务端的路由一致。
- 数据预取:在服务端预取数据后,将其传递到客户端。
SSR 如何在服务端预取数据?
- 使用 asyncData 或在路由守卫中预取数据:
js
const fetchData = async () => {
const data = await fetch('api/data')
return data.json();
};
服务端获取的数据如何传递给客户端?
- 使用 renderToString 时,将数据注入到 HTML:
js
<script>window.__INITIAL_STATE__ =${JSON.stringify(state)}
如何避免客户端与服务端状态不一致?
- 客户端同步:客户端在挂载时,通过 window.INITIAL_STATE 初始化状态。
Vue SSR 如何提升 SEO?
- 服务端返回的 HTML 包含完整内容,便于爬虫抓取。
- 配置动态 meta 标签(如 title 和 description)提升页面权重。
如何提高 Vue SSR 的性能?
- 使用缓存(如 Redis)减少重复渲染。
- 压缩 HTML 和资源文件。
- 避免不必要的组件加载,使用按需加载。
SSR 中如何避免重复渲染和多次数据请求?
- 确保数据预取只在服务端执行,客户端通过传递的状态初始化。
SSR 项目如何处理第三方库的依赖?
- 许多第三方库依赖 DOM,例如操作 document 或 window,在服务端渲染时可能会报错。
- 解决方案:在 mounted 中引入这些库,确保只在客户端运行。
如何解决 SSR 与浏览器特性冲突(如 window 或 document 不存在)?
- 浏览器特性冲突:○ 在代码中检查环境:
js
if (typeof window !== 'undefind')
// 浏览器相关代码
}
什么是闪屏问题?如何解决?
- 问题:服务端生成的 HTML 和客户端激活过程中样式或内容不一致。
- 解决方案:确保服务端和客户端使用相同的组件 、路由 和状态。
Nuxt.js 如何简化 Vue SSR 的实现?
- Nuxt.js 提供开箱即用的 SSR 支持,集成了路由、数据预取和打包优化。
Nuxt.js 的 asyncData 和 fetch 有什么区别?
- asyncData:在组件渲染前获取数据,用于静态内容。
- fetch:在客户端和服务端均可执行,用于动态内容。
如何配置 Nuxt.js 的动态路由和 SEO?
- 使用动态路由文件(pages/_id.vue)。
- 在页面中定义 head 方法动态配置 meta 信息:
js
export default {
head() {
return {
title: '动态标题',
meta: [{ name: 'description',content:'页面描述'}]
};
}
};
SSR 与 SSG 有什么区别?
在什么场景下应该选择 SSR 或 SSG?
SSR
- 每次请求动态生成页面。
- 适用于需要实时数据的应用,如电商、动态内容。
SSG
- 在构建时生成静态 HTML 文件,部署到 CDN。
- 适用于静态内容较多的应用,如博客、文档
面试题清单
- 什么是 SSR?与传统客户端渲染(CSR)有什么区别?
- SSR 的优点和缺点是什么?
- Vue SSR 的工作流程是什么?
- 为什么需要将 Vue 的组件和状态序列化到 HTML 中?
- 客户端和服务器端的应用实例有何不同?
- 如何使用 Vue 提供的 @vue/server-renderer 或 Nuxt.js 实现 SSR?
- 服务端渲染需要哪些基本配置?
- 如何处理路由和数据预取?
- SSR 如何在服务端预取数据?
- 服务端获取的数据如何传递给客户端?
- 如何避免客户端与服务端状态不一致?
- Vue SSR 如何提升 SEO?
- 如何提高 Vue SSR 的性能?
- SSR 中如何避免重复渲染和多次数据请求?
- SSR 项目如何处理第三方库的依赖?
- 如何解决 SSR 与浏览器特性冲突(如 window 或 document 不存在)?
- 什么是闪屏问题?如何解决?
- Nuxt.js 如何简化 Vue SSR 的实现?
- Nuxt.js 的 asyncData 和 fetch 有什么区别?
- 如何配置 Nuxt.js 的动态路由和 SEO?
- SSR 与 SSG 有什么区别?
- 在什么场景下应该选择 SSR 或 SSG?