延續之前的主題 Vuetify 中用到的技術大方向有:
今天要分享的是 vue-server-renderer,並且解釋在 Vuetify 的模板 WebpackSSR 是如何使用 vue-server-renderer。
還沒看前幾篇可以先從 Day19: Vuetify 開始閱讀。
這篇就不花時間解釋什麼是 Server-Side Rendering 了
npm install vue vue-server-renderer --save
注意:
就不廢話,直接進入基本範例:
因為範例會需要用到 express,開了一個 github repository,可以一鍵安裝看結果哦!
先附上 github vueServerRenderer-practice。
此範例會用到:
注意: node 版本要 6+
npm install
在本機渲染一個 Vue Instance。
index.js:
const Vue = require('vue')
const app = new Vue({
template: `<div>Hello World</div>`
})
const renderer = require('vue-server-renderer').createRenderer()
renderer.renderToString(app, (err, html) => {
if (err) throw err
console.log(html)
})
執行:
node index.js
結果:
<div data-server-rendered="true">Hello World</div>
透過 Express 建立一個最簡易的 server-renderer 應用
server.js:
const Vue = require('vue')
const server = require('express')()
const renderer = require('vue-server-renderer').createRenderer()
const PORT = 8080
server.get('*', (req, res) => {
const app = new Vue({
data: {
url: req.url
},
template: `<div>訪問的 URL 是: {{ url }}</div>`
})
renderer.renderToString(app, (err, html) => {
if (err) {
res.status(500).end('Internal Server Error')
return
}
res.writeHead(200, {
"Content-Type":"text/html;charset=UTF-8" // 避免亂碼
})
res.end(`
<!DOCTYPE html>
<html lang="zh-TW">
<head><title>Hello</title></head>
<body>${html}</body>
</html>
`)
})
})
server.listen(PORT, () => {
console.log(`Vue Server Renderer Example APP listening on port ${PORT}.`);
})
執行:
node server.js
結果:
Vue Server Renderer Example APP listening on port 8080.
請連結至 localhost:8080。
使用一個頁面模板(index.template.html),並插入值進去。
index.template.html:
<!DOCTYPE html>
<html lang="en">
<head>
<title>{{ title }}</title>
<!-- 雙括號為轉義HTML插值 -->
{{{ meta }}}
<!-- 三括號為不轉義HTML插值 -->
</head>
<body>
<!--vue-ssr-outlet-->
</body>
</html>
注意: 這個註解是用來標示應用程式注入 HTML 的地方。
serverTemplate.js:
const Vue = require('vue')
const server = require('express')()
const renderer = require('vue-server-renderer').createRenderer({
template: require('fs').readFileSync('./index.template.html', 'utf-8')
})
const PORT = 8080
const context = {
title: 'Hello',
meta: `
<meta ...>
<meta ...>
`
}
server.get('*', (req, res) => {
const app = new Vue({
data: {
url: req.url
},
template: `<div>訪問的 URL 是: {{ url }}</div>`
})
res.writeHead(200, {
"Content-Type":"text/html;charset=UTF-8" // 避免亂碼
})
renderer.renderToString(app, context, (err, html) => {
res.end(html) // will be the full page with app content injected.
})
})
server.listen(PORT, () => {
console.log(`Vue Server Renderer Example APP listening on port ${PORT}.`);
})
執行:
node serverTemplate.js
結果:
Vue Server Renderer Example APP listening on port 8080.
請連結至 localhost:8080。
回到 Vuetify 的目錄結構中,解釋一下模板 WebpackSSR 如何使用 vue-server-renderer。
就像是範例三,能夠在創建renderer時提供一個頁面模板,但 WebpackSSR 並沒有用到模板插值,所以相對簡單。
assets/index.template.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title></title>
<meta name="mobile-web-app-capable" content="yes">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui">
<meta name="description" content="A Vue.js project">
<meta name="keywords" content="">
<link rel="shortcut icon" href="/static/favicon.ico">
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" integrity="sha384-wvfXpqpZZVQGK6TAh5PVlGOfQNHSoD2xbE+QkPxCAFlNEevoEH3Sl0sibVcOQVnN" crossorigin="anonymous">
<link href='https://fonts.googleapis.com/css?family=Roboto:300,400,500,700|Material+Icons' rel="stylesheet" type="text/css">
</head>
<body>
<noscript id="deferred-styles"></noscript>
<!--vue-ssr-outlet-->
</body>
</html>
看一下 server.js 中的片段程式碼 :
const { createBundleRenderer } = require('vue-server-renderer')
const app = express()
const template = fs.readFileSync(resolve('./assets/index.template.html'), 'utf-8')
function createRenderer (bundle, options) {
// https://github.com/vuejs/vue/blob/dev/packages/vue-server-renderer/README.md#why-use-bundlerenderer
return createBundleRenderer(bundle, Object.assign(options, {
template,
cache: LRU({
max: 1000,
maxAge: 1000 * 60 * 15
}),
basedir: resolve('./public'),
runInNewContext: false
}))
}
let renderer
let readyPromise
if (isProd) {
const bundle = require('./public/vue-ssr-server-bundle.json')
const clientManifest = require('./public/vue-ssr-client-manifest.json')
renderer = createRenderer(bundle, {
clientManifest
})
} else {
readyPromise = require('./build/setup-dev-server')(app, (bundle, options) => {
renderer = createRenderer(bundle, options)
})
}
但 WebpackSSR 在這裡用的是 createBundleRenderer,而非範例三中的 createRenderer。
待補。
Vue.js Server-Side Rendering Guide