什么是 SSR
首先,Vue.js 是一个用于构建客户端应用的框架。默认情况下 Vue 组件的职责实在客户端浏览器中生成和操作 DOM。
然而,Vue 也支持将组件在服务端直接渲染成 HTML 字符串,并作为服务端响应返回给浏览器,最后在浏览器端将静态的 HTML“激活”为能够交互的客户端应用。————》这种服务端渲染技术就是 SSR。
为什么要用 SSR
优势:
- 更快的首屏加载,原因有二:
- 服务端渲染的 HTML 无需等到所有的 Javascript 都下载并执行完成之后才显示;
- 服务端更快的数据库连接(数据接口相关请求).
- 更好的 SEO,因为搜索引擎爬虫可以直接看到完全渲染的页面。
- 统一的心智模型,理解引用 Vue 官方的介绍:
你可以使用相同的语言以及相同的声明式、面向组件的心智模型来开发整个应用,而不需要在后端模板系统和前端框架之间来回切换。
限制和劣势:
- 开发中的限制:
- 浏览器端特定的代码只能在某些生命周期钩子中使用;
- 一些外部库可能需要特殊处理才能在服务端渲染的应用中运行。
- 更多的与构建配置和部署相关的要求。
- 服务端渲染的应用需要一个能让 Node.js 服务器运行的环境,不像完全静态的 SPA 那样可以部署在任意的静态文件服务器上。
- 更高的服务端渲染
- 在 Node.js 中渲染一个完整的应用要比仅仅托管静态文件更加占用 CPU 资源。
SSR vs SSG
SSG:静态站点生成,也被称为预渲染,是另一种流行的构建快速网站的技术。
SSR 与 SSG 相同之处:
- 都带来了优秀的首屏加载性能。
SSR 与 SSG 区别:
- SSG 比 SSR 应用的花销更小,也更容易部署,因为 SSG 输出的是静态 HTML 和资源文件,且 SSG 仅可以用于消费静态数据的页面。
基础教程
服务端渲染一个应用
-
第一步 创建一个新文件夹 vue-ssr,并在该文件夹下执行
npm init -y
生成 package.json,编辑 package.json 文件添加一行"type": "module"
,修改后的 package.json 文件内容如下:{ "name": "vue-ssr", "version": "1.0.0", "type": "module", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { "express": "^4.18.2", "vue": "^3.3.4" } }
-
第二步 安装依赖包
- 安装 vue
npm install vue
- 安装 express
npm install express
- 安装 vue
-
第三步 创建 server.js 文件,并写入如下代码:
import express from "express"; import { createSSRApp } from "vue"; // 导入Vue的服务端渲染API 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("Server Started...."); });
Vue 的服务端渲染 API
renderToString()
接受一个 Vue 应用实例作为参数,返回一个 Promise,当 Promise resolve 时返回应用渲染的 HTML。 -
第四步 运行
node server.js
启动服务,浏览器访问http://localhost:3000
,就可以看到页面中按钮了。
客户端激活
什么是客户端激活呢?
细心的同学可能已发现,上文运行服务端代码后,点击浏览器中的按钮数字并没有自增改变。这是因为这段 HTML 代码在客户端是完全静态的,并没有在浏览器中加载 Vue。
那么为了使客户端的应用可交互,Vue 需要执行一个激活步骤。
在激活过程中,Vue 会创建一个于服务端完全相同的应用实例,然后将每个组件与它应该控制的 DOM 节点相匹配,并添加 DOM 事件监听器。
由于实现激活需要重新组织代码结构(下文的代码结构部分会详解),所以此处先展示一个激活的代码片段:
// 该文件运行在浏览器中
import { createSSRApp } from "vue";
const app = createSSRApp({
// ...和服务端完全一致的应用实例
});
/**
* 在客户端挂载一个 SSR 应用时,会假定 HTML 是预渲染的,然后执行激活过程,而不是挂载新的 DOM 节点
*/
app.mount("#app");
注意 在激活模式下挂载应用,应该使用createSSRApp()
而不是createApp()
。
完整 SSR 代码结构
通用代码 app.js
通用代码只是指在客户端和服务端可复用的代码及其依赖项,我们可以把这些拆分到一个或多个独立的文件中。本例中代码逻辑简单,拆分一个 app.js 文件即可:
// app.js
import { createSSRApp } from "vue";
export function createApp() {
return createSSRApp({
data: () => ({ count: 1 }),
template: `<button @click="count++">{{ count }}</button>`,
});
}
客户端入口文件 client.js
// client.js
import { createApp } from "./app.js";
createApp().mount("#app");
服务端入口文件 server.js
运行服务端文件后,为了在浏览器中加载客户端文件,还需要在 server.js 文件中做以下修改:
- 增加一行
server.use(express.static('.'))
来托管客户端文件。 - 将
<script type="module" src="./client.js"></script>
添加到 HTML 外壳以加载客户端入口文件。 - 通过在 HTML 外壳文件中添加 Import Map 以支持在浏览器中使用
import * from 'vue'
。
修改后的 server.js 完整代码如下:
import express from "express";
import { renderToString } from "vue/server-renderer";
import { createApp } from "./app.js";
const server = express();
server.get("/", (req, res) => {
const app = createApp();
renderToString(app).then((html) => {
res.send(`
<!DOCTYPE html>
<html>
<head>
<title>Vue SSR Example</title>
<script type="importmap">
{
"imports": {
"vue": "https://unpkg.com/vue@3/dist/vue.esm-browser.js"
}
}
</script>
<script type="module" src="/client.js"></script>
</head>
<body>
<div id="app">${html}</div>
</body>
</html>
`);
});
});
server.use(express.static("."));
server.listen(3000, () => {
console.log("Server Started....");
});
此时我们一开始创建的文件夹下的代码结构如下:
vue-ssr
|-- app.js
|-- client.js
|-- server.js
|-- package.json
|-- package-lock.json
|-- node_modules
最后重新运行 node server.js
,再访问 http://localhost:3000
页面,点击按钮数字就会自增改变了。
通用解决方案
由于实现一套完整的 SSR 技术构建会非常复杂,因此建议和推荐大家在通过上面实例教程了解到 SSR 原理之后,选择一些成熟的 SSR 解决方案,以提高工作效率。下面推荐几个 Vue 生态中的 SSR 解决方案。
Nuxt 是一个构建于 Vue 生态系统之上的全栈框架,它为编写 Vue SSR 应用提供了丝滑的开发体验。更棒的是,你还可以把它当作一个静态站点生成器来用!我们强烈建议你试一试。
Quasar 是一个基于 Vue 的完整解决方案,它可以让你用同一套代码库构建不同目标的应用,如 SPA、SSR、PWA、移动端应用、桌面端应用以及浏览器插件。除此之外,它还提供了一整套 Material Design 风格的组件库。
- Vite SSR
Vite 提供了内置的 Vue 服务端渲染支持,但它在设计上是偏底层的。如果你想要直接使用 Vite,可以看看 vite-plugin-ssr,一个帮你抽象掉许多复杂细节的社区插件。