vue-server-renderer
用于 Vue2.0 的服務(wù)端渲染
這個包是自動生成的。如果你想合并請求請看 GigHub 上的 src/entries/web-server-renderer.js
這個包提供了基于 Node.js 的 Vue2.0 服務(wù)端渲染。
<a name="installation" href="#installation">安裝</a>
npm install vue-server-rederer
<a name="api" >API</a>
createRenderer([rendererOptions])
創(chuàng)建一個randerer實(shí)例。
const renderer = require('vue-server-renderer').createRenderer();
renderer.renderToString(vm,cb)
渲染一個string類型的 Vue 實(shí)例。它的回調(diào)函數(shù)是第一個參數(shù)為接受到的error對象的標(biāo)準(zhǔn) Node.js 的回調(diào)函數(shù)形式。
const Vue = require('vue')
const renderer = require('vue-server-renderer').createRenderer()
const vm = new Vue({
render (h) {
return h('div', 'hello')
}
})
renderer.renderToString(vm, (err, html) => {
console.log(html) // -> <div server-rendered="true">hello</div>
})
renderer.renderToStream(vm)
渲染一個流模式的 Vue 實(shí)例。返回一個 Node.js 的可讀流。
// 使用 express 的一個例子
app.get('/', (req, res) => {
const vm = new App({ url: req.url })
const stream = renderer.renderToStream(vm)
res.write(`<!DOCTYPE html><html><head><title>...</title></head><body>`)
stream.on('data', chunk => {
res.write(chunk)
})
stream.on('end', () => {
res.end('</body></html>')
})
})
createBundleRenderer(code,[rendererOptions])
通過預(yù)打包應(yīng)用程序代碼(請看服務(wù)端打包生成)來創(chuàng)建一個bundleRenderer實(shí)例。對于每一次渲染調(diào)用,使用 Node.js 的 vm 模塊實(shí)現(xiàn)代碼在新的上下文中重新執(zhí)行。這確保了你的應(yīng)用程序狀態(tài)是在請求之間是相互隔離的,并且你不用擔(dān)心為了服務(wù)端渲染而在一個限制模式下構(gòu)建你的應(yīng)用。
const bundleRenderer = require('vue-server-renderer').createBundleRenderer(code)
bundleRenderer.renderToString([context],cb)
渲染一個string類型的打包好的應(yīng)用。和renderer.renderToString相同的回調(diào)接口。這個上下文參數(shù)對象將會傳遞給打包的輸出函數(shù)。
bundleRenderer.renderToString({ url: '/' }, (err, html) => {
// ...
})
bundleRenderer.renderToString([context])
渲染一個流模式的打包好的應(yīng)用。和renderer.renderToStream想用的流式接口。這個上下文對象將會傳遞給打包的輸出函數(shù)。
bundleRenderer
.renderToStream({ url: '/' })
.pipe(writableStream)
<a name="renderer-options">Renderer 選項(xiàng)</a>
指令
允許你提供一些自定義指令用于服務(wù)端渲染的實(shí)現(xiàn)。
const renderer = createRenderer({
directives: {
example (vnode, directiveMeta) {
// transform vnode based on directive binding metadata
// 基于綁定元數(shù)據(jù)的指令轉(zhuǎn)化 vnode
}
}
})
還有一個例子,v-show's server-side implementation。
緩存
提供了一種組件緩存的實(shí)現(xiàn)。這個緩存對象必須實(shí)現(xiàn)下面的接口:
{
get: (key: string, [cb: Function]) => string | void,
set: (key: string, val: string) => void,
has?: (key: string, [cb: Function]) => boolean | void // optional
}
一個典型的應(yīng)用是傳一個 lrc-cache:
const LRU = require('lru-cache')
const renderer = createRenderer({
cache: LRU({
max: 10000
})
})
注意,緩存對象至少應(yīng)該設(shè)置get和set。另外,get方法如果提供了第二個參數(shù)作為回調(diào)函數(shù)那么還可以選擇異步使用。這允許緩存使用異步APIs,例如redis client 例子:
const renderer = createRenderer({
cache: {
get: (key, cb) => {
redisClient.get(key, (err, res) => {
// handle error if any
cb(res)
})
},
set: (key, val) => {
redisClient.set(key, val)
}
}
})
<a name="why-use-bundlerrenderer">為什么使用 bundleRenderer?</a>
在一個典型的 Node.js 應(yīng)用中,后端服務(wù)是一個長時間運(yùn)行的進(jìn)程。如果我們直接請求我們的應(yīng)用代碼,實(shí)例化模塊將會在每個請求中共享。這帶了一些不方便的限制在我們構(gòu)建時:比如我們必須避免使用全局狀態(tài)單例(例如:Vuex里面的store),否則每一次狀態(tài)改變都會導(dǎo)致下一次請求被影響。
相反,bundleRenderer更容易保證我們的每一個請求在運(yùn)行的應(yīng)用程序中都是“新的”(即實(shí)例化模塊不共享),這樣我們不需要去考慮通過每次請求避免狀態(tài)污染的問題了。這些都是bundleRenderer幫助我們實(shí)現(xiàn)的。
<a name="creating-the-server-bundle">服務(wù)端打包生成</a>

應(yīng)用程序的打包可以通過任何構(gòu)建工具生成,你可以使用很簡單的 Webpack + vue-loader 還有 bundleRenderer實(shí)現(xiàn)。你需要使用略微不同的 webpack 配置和為了服務(wù)端渲染的入口文件,但是這個不同是很小的:
為你的 webpack 配置添加
target:'node'和output:{libraryTarget:'commonjs2'}。這可能是一個比較好的處理你的外部依賴文件的方法。在你的服務(wù)端入口文件拋出一個方法。這個方法將會接收到渲染的上下文對象(傳遞給
bundleRenderer.renderToString或者bundleRenderer.renderToStream),并且返回一個Promise,這將最終解決應(yīng)用程序 Vue 根實(shí)例的問題。
// server-entry.js
import Vue from 'vue'
import App from './App.vue'
const app = new Vue(App)
// 默認(rèn)的輸出應(yīng)該是一個接收渲染調(diào)用時上文對象的函數(shù)。
export default context => {
// data pre-fetching
return app.fetchServerData(context.url).then(() => {
return app
})
}
外部依賴
我們在使用bundleRenderer時,最好在服務(wù)端打包時通過默認(rèn)的打包方式把每一個依賴文件打進(jìn)我們的應(yīng)用程序。這意味著在每次請求那些依賴文件時將需要解析和運(yùn)行一遍,但是在大多數(shù)情況下這些是不需要的。
我們能夠通過你打的包來優(yōu)化這些外部依賴關(guān)系。在渲染過程中,任何原始的require()調(diào)用都將返回實(shí)際的 Node 模塊從你的渲染進(jìn)程中。使用 webpack ,我們可以很簡單的通過externals配置選項(xiàng)來列舉出那些我們想要處理的外部依賴模塊。
// webpack.config.js
module.exports = {
// 這些外部依賴中的所有模塊都在你的package.json文件中的 “dependencies” 下面
externals: Object.keys(require('./package.json').dependencies)
}
外部警告
由于外部依賴模塊在每個請求中是共享的,所以你必須保證這些依賴關(guān)系是等同的。這樣,通過不同的請求應(yīng)該總是返回相同的結(jié)果,并且它不能擁有通過你的應(yīng)用程序來改變的全局狀態(tài)(如:使用 Vue 插件)。
<a name="component-caching">組件緩存</a>
你可以容易的通過serverCacheKyy函數(shù)來在服務(wù)端渲染中來緩存組件:
export default {
name: 'item', // required
props: ['item'],
serverCacheKey: props => props.item.id,
render (h) {
return h('div', this.item.id)
}
}
注意緩存組件必須配置唯一的“name”選項(xiàng)。這對于 Vue 在使用打包渲染時確定組件的身份來說是必須的。
使用唯一的名字來作為緩存組件的的鍵名:你不用擔(dān)心兩個組件返回相同的鍵。一個緩存組件的鍵名應(yīng)該包含足夠的信息來表述它渲染的結(jié)果。上面的這種方式是一種很好的實(shí)現(xiàn)如果這個渲染結(jié)果可以通過props.item.id能完全確定。然而,這個組件隨著時間的推移原本的 ID 可能會改變,或者渲染結(jié)果還依賴另一個prop,這樣你需要去修改你的getCacheKey來實(shí)現(xiàn)獲取其他的變量在程序中。
返回一個常數(shù)總會被緩存,這對于純粹的靜態(tài)組件來說是很好的。
什么時候使用組件緩存?
如果渲染器在渲染期間渲染了一個組件,它將直接為真?zhèn)€子樹重用緩存結(jié)果。所以不要緩存包含全局狀態(tài)的子組件。
在大多數(shù)情況下,你不應(yīng)該和不需要緩存簡單的實(shí)例組件。最常見的組件緩存需要大名單。由于這些組件通常是一些由數(shù)據(jù)庫中的對象集合驅(qū)動的,他們可以使用一些簡單的緩存策略。生成它們的緩存鍵名使用它們自己的唯一ID加最后更新的時間戳。
serverCacheKey: props => props.item.id + '::' + props.item.last_updated
<a name="client-side-hydration">客戶端合成</a>
在服務(wù)端渲染輸出,根元素將會有一個server-rendered=true的屬性標(biāo)記。在客戶端,當(dāng)你使用這個屬性掛載一個 Vue 實(shí)例到元素上時,它將嘗試合成到現(xiàn)有的DOM實(shí)例而不是創(chuàng)建新的DOM節(jié)點(diǎn)。
在開發(fā)模型中,Vue 將維護(hù)客戶端生成的虛擬DOM樹來匹配來自服務(wù)端渲染的DOM結(jié)構(gòu)。如果不匹配,它將放棄合成,維持現(xiàn)有DOM并且從頭開始渲染。在生產(chǎn)模型下,這種維護(hù)是被禁用的為了更高的性能。
合成警告
有一些事情需要特別注意,當(dāng)使用服務(wù)端渲染+客戶端合成一些特殊HTML結(jié)構(gòu)時,瀏覽器可能會改變HTML結(jié)構(gòu)。比如,當(dāng)你寫下面這個樣的Vue實(shí)例的時候:
<table>
<tr><td>hello word</td></tr>
</table>
瀏覽器會自動添加tbody到table中,然而,Vue生成的虛擬DOM不會包含tbody,所以將會導(dǎo)致不匹配。為了確保正確的匹配,請準(zhǔn)確書寫有效的HTML在你的模板中。
說明
本文為個人在學(xué)習(xí) Vue 服務(wù)端渲染時,翻譯自npm:vue-server-renderer的README文檔,本人初學(xué) Vue 能力有限,翻譯有誤地方請大家指出。
另外,如果想學(xué)習(xí)Vue官方的服務(wù)端渲染的例子vue-ssr-demo-simple和vue-hackernews-2.0的例子,而又對各種webpack配置和英文注釋有些迷茫,可以看一下我對官方vue-ssr-demo-simple和vue-vue-hackernews-2.0一個注釋版本,根據(jù)自己的理解加的注釋。當(dāng)然個人能力有限,可能有些地方理解有誤,還請諒解