近期须要接手一个vue ssr项目,因为本人以前没有写过ssr,只是稍微了解了点。因此跟着官网学了下,并整理出了这篇学习笔记。方便本身之后对vue ssr知识的回顾。好记性不如烂笔头。javascript
相信你们在看到这篇文章以前,都知道ssr是什么了。SSR,英文全称叫 Server(服务) side(端) rendering (渲染)哈哈☺html
那么究竟什么是服务器端渲染?vue
Vue.js 是构建客户端应用程序的框架。默认状况下,能够在浏览器中输出 Vue 组件,进行生成 DOM 和操做 DOM。然而,也能够将同一个组件渲染为服务器端的 HTML 字符串,将它们直接发送到浏览器,最后将这些静态标记"激活"为客户端上彻底可交互的应用程序。服务器渲染的 Vue.js 应用程序也能够被认为是"同构"或"通用",由于应用程序的大部分代码均可以在服务器和客户端上运行。java
若是你问我为何使用ssr呢?(具体可参考官网)node
- 有利于seo。
- 更快的内容到达时间 (time-to-content),特别是对于缓慢的网络状况或运行缓慢的设备。大致能够理解为渲染出页面时间,csr比ssr多了个js下载时间。由于ssr一开始加载下来就渲染出来了,而后在下载激活html的js。csr是下载完在渲染。
ssr主要依靠两个包vue-server-renderer
和 vue
(两个版本必须匹配)express
安装: npm install vue vue-server-renderer --save
npm
// server.js const server = require('express')() const Vue = require('vue'); const renderer = require('vue-server-renderer').createRenderer(); server.get('*', (req, res) => { const context = { url: req.url } const app = new Vue({ template: `<div>${context.url}</div>` }) renderer.renderToString(app, (err, html) => { if (err) { res.status(500).end('Internal Server Error') return } res.end(` <!DOCTYPE html> <html lang="en"> <head><title>Hello</title></head> <body>${html}</body> </html> `) }) }) server.listen(8080)
node server.js
浏览器输入localhost:8080访问该ssr页面segmentfault
这时候你能够看到,不管你输入什么路径,页面文本都会显示出你的路径浏览器
当你在渲染 Vue 应用程序时,renderer 只从应用程序生成 HTML 标记 (markup)。在这个示例中,咱们必须用一个额外的 HTML 页面包裹容器,来包裹生成的 HTML 标记。纯客户端渲染的时候,会有一个模板,会插入你打包后的一些文件等。那么ssr会不会也有这种模板呢?固然会有。服务器
首先在根目录下新建一个index.template.html
文件
<!DOCTYPE html> <html lang="en"> <head><title>Hello</title></head> <body> <!--vue-ssr-outlet--> </body> </html>
注意了 --跟vue或者outlet跟--之间不能用空格。注释 -- 这里将是应用程序 HTML 标记注入的地方。
接下来,修改下刚才的server.js文件后以下
const server = require('express')() const Vue = require('vue'); const renderer = require('vue-server-renderer').createRenderer({ template: require('fs').readFileSync('./index.template.html', 'utf-8') }); server.get('*', (req, res) => { const context = { url: req.url } const app = new Vue({ template: `<div>${context.url}</div>` }) renderer.renderToString(app, (err, html) => { if (err) { res.status(500).end('Internal Server Error') return } res.end(html) }) }) server.listen(8080)
就是在createRenderer中多加一个参数 template(读取模板文件),并传递给createRenderer方法
模板还支持插值
<html> <head> <!-- 使用双花括号(double-mustache)进行 HTML 转义插值(HTML-escaped interpolation) --> <title>{{ title }}</title> <!-- 使用三花括号(triple-mustache)进行 HTML 不转义插值(non-HTML-escaped interpolation) --> {{{ meta }}} </head> <body> <!--vue-ssr-outlet--> </body> </html>
咱们能够经过传入一个"渲染上下文对象",做为 renderToString
函数的第二个参数,来提供插值数据:
const context = { title: 'hello', meta: ` <meta ...> <meta ...> ` } renderer.renderToString(app, context, (err, html) => { // 页面 title 将会是 "Hello" // meta 标签也会注入 })
咱们以往的纯浏览器渲染都是把js下载到本地执行的。上述代码你会发现都是用的同一个Vue构造函数,可是想对该构造函数作特殊处理时,就会对其余用户形成污染。所以,咱们不该该直接建立一个应用程序实例,而是应该暴露一个能够重复执行的工厂函数,为每一个请求建立新的应用程序实例:
// 修改原先代码以下 -const Vue = require('vue'); +const createApp = require('./app.js') - const app = new Vue({ - template: `<div>${context.url}</div>` - }) + const { app } = createApp(context) // 新增app.js const Vue = require('vue'); module.exports = function createApp(context) { const app = new Vue({ template: `<div>${context.url}</div>` }) return { app } }
这样,每次访问该服务器的时候,都会生成一个新的vue实例。一样的规则也适用于 router、store 和 event bus 实例。你不该该直接从模块导出并将其导入到应用程序中,而是须要在 createApp
中建立一个新的实例,并从根 Vue 实例注入。