一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第6天,点击查看活动详情。
简介及优缺点
服务端渲染SSR(Server side Render):后端渲染出完整的DOM并返回,前端拿到内容包括首屏及spa结构,应用激活后依然按照spa的方式运行,这种渲染的页面方式称作为服务端渲染(SSR)
- 与SSR的区别
- 传统web开发
传统web的开发,网页内容在服务端渲染完成,一次性传输到浏览器,其中包括查询数据库拼接html字符串(模板),
浏览器拿到的时全部的dom结构
- 单页面应用Single Page App
单页面应用有着优秀的用户的体验,使其逐步成为主流,页面内容由JS渲染出来,这种方式称为客户端渲染,
浏览器只拿到仅有的宿主元素#app,并没有内容
- 优点
- 方便SEO优化,保证浏览器搜索引擎爬虫抓取主要内容数据
- 响应更快,浏览器只需解析DOM,并直接构建DOM树即可
- 缺点
- 渲染计算要放到客户端进行,会消耗CPU和内存资源
- 浏览器一些常用的api,无法使用
应用
创建工程和安装依赖
- vue-cli创建工程
vur create ssr
- 安装依赖 要确保vue、vue-server-renderer的版本一致
npm install vur-server-renderer
启动
- 创建一个express服务器,将vue ssr集成进来
const express = require("express");
const app = express()
- 导入VUE构造函数
const Vue = require("vue");
- createRenderer获取渲染器
const { createRenderer } = require("vue-server-renderer")
const renderer = createRenderer()
- 创建一个待渲染的Vue实例(模板)
用new Vue创建一个template模板并设置data参数,renderTostring将vue实例渲染为html字符串、并返回一个promise,最后返回到客户端
(res.send()),如果渲染出错返回500错误,并返回,这里用tryCatch,防止报错不往下进行
app.get("/", async (req, res) => {
const vm = new Vue({
data: { name: "hello" },
template: `
<div >
<h1>{{name}}</h1>
</div>`
});
try {
const html = await renderer.renderToString(vm);
res.send(html);
} catch (error) {
res.status(500).send("Internal Server Error");
}
});
路由
SSR支持vur-router所以咱们继续使用这个
- 创建路由实例
- 引入vue-router
const Router = require('vue-router')
Vue.use(Router)
- 挂载
在内通过new Router创建路由对象并在内配置路由
routers:[{path:''}]等和传统的一样,随后在内创建vue实例引入router-link和router-vuew,并将它挂载上去
app.get('*', async function (req, res) {
const vm = new Vue({
data: { msg: 'hello' },
template: `
<div>
<router-link to="/">index</router-link>
<router-view></router-view>
</div>`,
router,
})
try {
router.push(req.url);
const html = await renderer.renderToString(vm)
res.send(html)
} catch (error) {
res.status(500).send('渲染出错')
}
})
通过try Catch判断跳转至相应的路由并开始渲染模板并返回给客户端,如果渲染出错返回500返回给客户端
同构开发ssr流程
对于同构开发,我们依然使用webpack进行打包,其中需要解决两个问题,服务端渲染和客户端激活
- 构建流程 目标是生成一个服务器bundle,用于服务器首屏渲染,和一个客户端bundle用于客户端激活
- 代码结构 除了两个不同入口之外,其他的结构和之前vue完全一致
src
├── router
├────── index.js # 路由声明
├── store
├────── index.js # 全局状态
├── main.js # ⽤于创建vue实例
├── entry-client.js # 客户端⼊⼝,⽤于静态内容“激活”
└── entry-server.js # 服务端⼊⼝,⽤于⾸屏内容渲染
- 服务端入口 上面的bundel就是webpack打包服务器的bundel,我们需要编写服务器入口文件src/entry-server.js它的任务是创建vue实例并根据数据传入url指定首屏
import { createApp } from "./main";
export default context => {
return new Promise((resolve, reject) => {
const { app, router } = createApp(context);
// 跳转到⾸屏的地址
router.push(context.url);
// 路由就绪,返回结果
router.onReady(() => {
resolve(app);
}, reject);
});
};
它需要返回一个函数,接收请求的上下文context,返回一个创建vue的实例,在内返回一个Promise,确保路由或者组件准备就绪,在Promise内设置跳转到首屏的地址,随后挂载并返回挂载结果。
- 客户端入口 客户端入口只需要创建vue实例并执行挂载,这步成为激活,创建entry-client.js
import { createApp } from "./main";
const { app, router } = createApp();
router.onReady(() => {
app.$mount("#app");
});
引入createApp并创建vue和router实例,路由准备就绪后,执行挂载
运行
npm run build:client //构建客户端
npm run build:serve //构建服务端