近期須要接手一個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 實例注入。