vue ssr入門
前言
近期需要接手一個vue ssr項目,由於本人之前沒有寫過ssr,只是稍微瞭解了點。所以跟着官網學了下,並整理出了這篇學習筆記。方便自己以後對vue ssr知識的回顧。好記性不如爛筆頭。
介紹
相信大家在看到這篇文章之前,都知道ssr是什麼了。SSR,英文全稱叫 Server(服務) side(端) rendering (渲染)哈哈☺
那麼究竟什麼是服務器端渲染?
Vue.js 是構建客戶端應用程序的框架。默認情況下,可以在瀏覽器中輸出 Vue 組件,進行生成 DOM 和操作 DOM。然而,也可以將同一個組件渲染爲服務器端的 HTML 字符串,將它們直接發送到瀏覽器,最後將這些靜態標記"激活"爲客戶端上完全可交互的應用程序。服務器渲染的 Vue.js 應用程序也可以被認爲是"同構"或"通用",因爲應用程序的大部分代碼都可以在服務器和客戶端上運行。
如果你問我爲什麼使用ssr呢?(具體可參考官網)
- 有利於seo。
- 更快的內容到達時間 (time-to-content),特別是對於緩慢的網絡情況或運行緩慢的設備。大體可以理解爲渲染出頁面時間,csr比ssr多了個js下載時間。因爲ssr一開始加載下來就渲染出來了,然後在下載激活html的js。csr是下載完在渲染。
正文
基本用法
ssr主要依靠兩個包vue-server-renderer
和 vue
(兩個版本必須匹配)
安裝: npm install vue vue-server-renderer --save
入門配置
ssr最簡易配置
// 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頁面
這時候你可以看到,無論你輸入什麼路徑,頁面文本都會顯示出你的路徑
ssr使用模板
當你在渲染 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 實例注入。