一.什么是SSR
在浏览器请求页面输入URL的时候,组件将渲染为服务器端的 HTML 字符串,将它们直接发送到浏览器,最后将这些静态标记"激活"为客户端上完全可交互的应用程序
-
怎么渲染成HTML字符串?【renderTostring()】
-
怎么把HTML字符串激活?【app.$mount('#app')】
二.为什么使用SSR
1.传统的服务端渲染
使用的是后端模版渲染【如jsp或php】使用模板引擎将模板与数据拼接成完整的HTML
缺点:
- a-前后端职责不清楚
- b-前后端代码杂糅在一起
- c-项目难以管理维护
优点:
- a-减少白屏的时间
- b-seo友好(html带有页面需要的内容)
2.客户端渲染 CSR
用js来渲染页面大部分内容,spa单页面应用(如:react,vue)在客户端请求时,服务端不做任何处理,将前端打包好后生成的HTML反给客户端
补:Vue组件渲染流程
组件声明-组件创建-渲染
组件声明:Vue的全局静态方法Vue.component进行注册,组件内部利用components属性进行注册,组件是可复用的 Vue 实例【Vue.component继承Vue】
组件创建:组件注册之后就会在模板里进行声明使用,模板里声明使用的组件最后回被vue-loader编译返回一个渲染函数render
- a-调用createElement创建节点
- b-自定义你组件调用【createComponent-installComponentHooks-componentVNodeHooks源码的大体调用步骤,也是创建节点】
渲染:$mount-patch-createEle 【递归的将组件里的vnode渲染成正式DOM】
问题:
- a-首屏加载慢
- b-SEO不友好
- c-客户端渲染至少发起三次请求【第一次请求页面,第二次请求页面里面的js,第三次动态数据请求】
- d-需要重复渲染【发送请求获取后台数据一般在mounted,页面已经渲染一次,获取到数据,更新视图,页面又需要再渲染一次】
优点:
- a-前后端代码解藕
- b-可以实现局部刷新【两端分别请求】
3.同构渲染 SSR
相同代码在服务端运行一遍,在达浏览器又运行一遍【服务端渲染完成页面结构,客户端渲染绑定事件】
问题:
- a-开发条件首先【客户端和服务端渲染在写法上的区别容易造成bug】
- b-项目构建和部署【依赖于node环境】
- c-更多的服务器负载
优点:
- a-更好的SEO
- b-更快的加载速度
- c-前后端代码解藕
三.Vue SSR实战
在此之前应该先了解Vue SSR指南ssr.vuejs.org/zh/
上图是Vue官方的SSR原理介绍图片。从这张图片,我们可以知道:我们需要通过Webpack打包生成两份bundle文件:
- Client Bundle,给浏览器用。和纯Vue前端项目Bundle类似
- Server Bundle,供服务端SSR使用,一个json文件
1.初始化项目
mkdir vue-ssr
cd vue-ssr
npm init -y
npm i vue vue-server-renderer
2.创建server.js(在根目录下)
// 第 1 步:创建一个 Vue 实例
const Vue = require('vue')
const app = new Vue({
template: `
<div id="app">
<h1>{{ message }}</h1>
</div>
`,
data: {
message: 'MESSAGE'
}
})
// 第 2 步:创建一个 renderer
const renderer = require('vue-server-renderer').createRenderer()
// 第 3 步:将 Vue 实例渲染为 HTML
renderer.renderToString(app, (err, html) => {
if (err) throw err
console.log(html)
// => <div id="app" data-server-rendered="true"><h1>时光海</h1></div>
})
与服务器集成
npm run express
当前server.js
const express = require('express')
const server = express()
server.get('/', (req, res) => {
const app = new Vue({
template: `
<div id="app">
<h1>{{ message }}</h1>
</div>
`,
data: {
message: 'MESSAGE'
}
})
const renderer = require('vue-server-renderer').createRenderer()
renderer.renderToString(app, (err, html) => {
if (err) {
res.status(500).end('Internal Server Error.')
}
// 解决编码问题
res.setHeader('Content-Type', 'text/html; charset=utf-8')
res.end(html)
})
})
server.listen(3000, () => {
console.log('server runing at port 3000.')
})
启动服务器
nodemon server.js
4.创建模版index.template.html
<!DOCTYPE html>
<html lang="en">
<head>
<title>Hello</title>
<meta charset="UTF-8">
</head>
<body>
<!-- html字符串注入 -->
<!--vue-ssr-outlet-->
</body>
</html>
再模板中使用外部的数据
当前的server.js
const express = require('express')
const fs = require('fs')
const renderer = require('vue-server-renderer').createRenderer({
template: fs.readFileSync('./index.template.html',
'utf-8')
})
const server = express()
server.get('/', (req, res) => {
const app = new Vue({
template: `
<div id="app">
<h1>{{ message }}</h1>
</div>
`,
data: {
message: 'MESSAGE'
}
})
renderer.renderToString(app, {
title: 'VUE_SSR',
meta: `
<meta name="description" content="MESSAGE">
`
}, (err, html) => {
if (err) {
res.status(500).end('Internal Server Error.')
}
// 解决编码问题
res.setHeader('Content-Type', 'text/html; charset=utf-8')
res.end(html)
}
)
})
server.listen(3000, () => {
console.log('server runing at port 3000.')
})
当前index.template.html
<!DOCTYPE html>
<html lang="en">
<head>
<title>{{ title }}</title>
<meta charset="UTF-8">
{{{ meta }}}
</head>
<body>
<!-- html字符串注入 -->
<!--vue-ssr-outlet-->
</body>
</html>
构建同构渲染 源码查看github.com/EdaDuan/vue…
在demo中有注释 可以直接pull下来运行