SSR 解决了什么问题,有做过 SSR 吗?
1. 什么是 SSR
SSR(Server-Side Rendering)意为服务端渲染,指由服务侧完成页面的 HTML 结构拼接的页面处理技术,发送到浏览器,然后为其绑定状态与事件,成为完全可交互页面的过程。
打开页面查看源码,浏览器拿到的是全部的 DOM 结构。
Web 发展的 3 个阶段
- 服务端渲染(SSR):服务端直接吐出完整 HTML,浏览器拿到的内容包括首屏及完整结构。
- 单页应用(SPA):浏览器拿到的仅有宿主元素
#app,其余内容由 JS 运行时渲染。 - SSR + SPA 同构:后端渲染出完整的首屏 DOM 结构返回,前端拿到内容包括首屏及完整 SPA 结构,应用激活后依然按照 SPA 方式运行。
2. SSR 解决了什么
SSR 主要解决了以下两种问题:
- SEO 问题:SPA 首屏 HTML 内容为空,搜索引擎爬虫无法抓取到关键内容,影响收录。SSR 直接吐出完整 HTML,对爬虫友好。
- 首屏白屏问题:SPA 需要先下载 JS 再执行渲染,首屏出现明显白屏。SSR 直接返回首屏内容,TTFB 到首屏可见的时间更短。
SSR 的缺点
- 开发条件受限:浏览器端特有的代码(
window/document)只能放在beforeMount/mounted等生命周期里;一些外部库可能需要特殊处理。 - 更多的服务端负载:在服务端渲染时需要占用 CPU 资源,如果访问量大且复杂,可能会占用大量 CPU。
- 部署 / 运维复杂:需要同时维护 Node 服务和静态服务。
选型前要慎重考虑的问题
- 是否真的需要 SEO?
- 首屏可见时间对业务的关键程度?
- 团队是否具备 Node 端开发和运维能力?
3. 如何实现 SSR
对于同构开发,我们依然使用 webpack 打包,但要解决两个问题:服务端首屏渲染 和 客户端激活。
这里需要生成一个服务器 bundle 文件用于服务端首屏渲染,和一个客户端 bundle 文件用于客户端激活。
代码结构
除了两个不同入口(客户端入口和服务端入口),其他结构和之前的 vue 应用完全相同:
src
├── router
│ └── index.js # 路由声明
├── store
│ └── index.js # 全局状态
├── main.js # 用于创建 vue 实例
├── entry-client.js # 客户端入口,用于静态内容"激活"
└── entry-server.js # 服务端入口,用于首屏内容渲染
主文件
import Vue from 'vue'
import App from './App.vue'
import { createRouter } from './router'
import { createStore } from './store'
// 导出 Vue 实例工厂函数,为每次请求创建独立实例
// 上下文用于给 vue 实例传递参数
export function createApp(context) {
const router = createRouter()
const store = createStore()
const app = new Vue({
router,
context,
render: h => h(App)
})
return { app, router, store }
}
服务端入口
它的任务是创建 Vue 实例并根据传入的 url 指定首屏:
import { createApp } from './main'
// 返回一个函数,接收请求上下文,返回创建的 vue 实例
export default context => {
return new Promise((resolve, reject) => {
const { app, router } = createApp(context)
// 跳转到首屏的地址
router.push(context.url)
// 路由就绪,返回结果
router.onReady(() => {
resolve(app)
}, reject)
})
}
客户端入口
客户端入口只需创建 vue 实例并执行挂载,这一步称为"激活":
import { createApp } from './main'
const { app, router } = createApp()
router.onReady(() => {
app.$mount('#app')
})
注意:服务器端渲染的是应用程序的"快照"。如果应用依赖于异步数据,那么在开始渲染之前,需要先预取和解析好这些数据。
来源整理自:vue3js.cn 面试官系列



