iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 22
0
Modern Web

用範例理解 Vue.js系列 第 22

用範例理解 Vue.js #22:vue-server-renderer in Vuetify

Imgur

圖片來源

延續之前的主題 Vuetify 中用到的技術大方向有:

  • vue-router
  • vuex
  • vue-server-renderer
  • webpack
  • express

今天要分享的是 vue-server-renderer,並且解釋在 Vuetify 的模板 WebpackSSR 是如何使用 vue-server-renderer。

還沒看前幾篇可以先從 Day19: Vuetify 開始閱讀。

vue-server-renderer 是什麼?

這篇就不花時間解釋什麼是 Server-Side Rendering 了

安裝

npm install vue vue-server-renderer --save

注意:

  • vue 和 vue-server-renderer 的版本(2.3.0+)需互相匹配。
  • 目前 vue-server-renderer 依賴一些 Node.js 原生 modules,所以只能再 Node.js 環境下使用。

就不廢話,直接進入基本範例:

因為範例會需要用到 express,開了一個 github repository,可以一鍵安裝看結果哦!

先附上 github vueServerRenderer-practice

此範例會用到:

  • vue
  • vue-server-renderer
  • express

注意: node 版本要 6+

使用

  • 安裝依賴的 node_modules
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

vue-server-renderer in Vuetify template WebpackSSR

回到 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


上一篇
用範例理解 Vue.js #21:Vuex in Vuetify
下一篇
用範例理解 Vue.js #23:webpack in Vuetify
系列文
用範例理解 Vue.js30

尚未有邦友留言

立即登入留言