SSR(服务器渲染)
# SSR(服务器渲染)
# 什么是浏览器端渲染 (CSR)?
CSR是Client Side Render简称;页面上的内容是我们加载的js文件渲染出来的,js文件运行在浏览器上面,服务端只返回一个html模板。
# 什么是 SSR?
SSR是Server Side Render简称;页面上的内容是通过服务端渲染生成的,浏览器直接显示服务端返回的html就可以了。
默认情况下,Vue.js可以在浏览器中输出 Vue 组件,进行生成 DOM 和操作 DOM。然而也可以将同一个组件渲染为服务器端的 HTML 字符串,将它们直接发送到浏览器,最后将这些静态标记"激活"为客户端上完全可交互的应用程序。
服务器渲染的 Vue.js 应用程序也可以被认为是"同构"或"通用",因为应用程序的大部分代码都可以在服务器和客户端上运行。
# 为什么要用 SSR?
与客户端的单页应用 (SPA) 相比,SSR 的优势主要在于:
更快的首屏加载:这一点在慢网速或者运行缓慢的设备上尤为重要。
统一的心智模型:你可以使用相同的语言以及相同的声明式、面向组件的心智模型来开发整个应用,而不需要在后端模板系统和前端框架之间来回切换。
更好的 SEO:搜索引擎爬虫可以直接看到完全渲染的页面。
# SSR vs. SSG
静态站点生成 (Static-Site Generation,缩写为 SSG),也被称为预渲染,是另一种流行的构建快速网站的技术。如果用服务端渲染一个页面所需的数据对每个用户来说都是相同的,那么我们可以只渲染一次,提前在构建过程中完成,而不是每次请求进来都重新渲染页面。预渲染的页面生成后作为静态 HTML 文件被服务器托管。
这里的关键词是静态:SSG 仅可以用于消费静态数据的页面,即数据在构建期间就是已知的,并且在多次部署期间不会改变。每当数据变化时,都需要重新部署。
# 基础教程
渲染一个应用
让我们来看一个 Vue SSR 最基础的实战示例。
- 创建一个新的文件夹,
cd
进入 - 执行
npm init -y
- 在
package.json
中添加"type": "module"
使 Node.js 以 ES modules mode (opens new window) 运行 - 执行
npm install vue
- 创建一个
example.js
文件:
// 此文件运行在 Node.js 服务器上
import { createSSRApp } from 'vue'
// Vue 的服务端渲染 API 位于 `vue/server-renderer` 路径下
import { renderToString } from 'vue/server-renderer'
const app = createSSRApp({
data: () => ({ count: 1 }),
template: `<button @click="count++">{{ count }}</button>`
})
renderToString(app).then((html) => {
console.log(html)
})
接着运行:
> node example.js
它应该会在命令行中打印出如下内容:
<button>1</button>
然后我们可以把 Vue SSR 的代码移动到一个服务器请求处理函数里,它将应用的 HTML 片段包装为完整的页面 HTML。接下来的几步我们将会使用 express
(opens new window):
- 执行
npm install express
- 创建下面的
server.js
文件:
import express from 'express'
import { createSSRApp } from 'vue'
import { renderToString } from 'vue/server-renderer'
const server = express()
server.get('/', (req, res) => {
const app = createSSRApp({
data: () => ({ count: 1 }),
template: `<button @click="count++">{{ count }}</button>`
})
renderToString(app).then((html) => {
res.send(`
<!DOCTYPE html>
<html>
<head>
<title>Vue SSR Example</title>
</head>
<body>
<div id="app">${html}</div>
</body>
</html>
`)
})
})
server.listen(3000, () => {
console.log('ready')
})
最后,执行 node server.js
,访问 http://localhost:3000
。你应该可以看到页面中的按钮了。
# 客户端激活
如果你点击该按钮,你会发现数字并没有改变。这段 HTML 在客户端是完全静态的,因为我们没有在浏览器中加载 Vue。
为了使客户端的应用可交互,Vue 需要执行一个激活步骤。
在激活过程中,Vue 会创建一个与服务端完全相同的应用实例,然后将每个组件与它应该控制的 DOM 节点相匹配,并添加 DOM 事件监听器。
为了在激活模式下挂载应用,我们应该使用 createSSRApp()
(opens new window) 而不是 createApp()
:
# 代码结构
我们如何在服务器和客户端之间共享相同的应用代码呢?
这里我们将演示最基础的设置。首先,让我们将应用的创建逻辑拆分到一个单独的文件 app.js
中:
// app.js (在服务器和客户端之间共享)
import { createSSRApp } from 'vue'
export function createApp() {
return createSSRApp({
data: () => ({ count: 1 }),
template: `<button @click="count++">{{ count }}</button>`
})
}
该文件及其依赖项在服务器和客户端之间共享——我们称它们为通用代码。
我们在客户端入口导入通用代码,创建应用并执行挂载:
// client.js
import { createApp } from './app.js'
createApp().mount('#app')
服务器在请求处理函数中使用相同的应用创建逻辑:
// server.js (不相关的代码省略)
import { createApp } from './app.js'
server.get('/', (req, res) => {
const app = createApp()
renderToString(app).then(html => {
// ...
})
})
此外,为了在浏览器中加载客户端文件,我们还需要:
- 在
server.js
中添加server.use(express.static('.'))
来托管客户端文件。 - 将
<script type="module" src="/client.js"></script>
添加到 HTML 外壳以加载客户端入口文件。 - 通过在 HTML 外壳中添加 Import Map (opens new window) 以支持在浏览器中使用
import * from 'vue'
。