天天看點

使用Vue-ssr提供服務端渲染html卡片服務什麼是伺服器端渲染(SSR)搭建vue工程實作服務端渲染編寫服務端渲染的卡片模闆

本文記錄一個實作服務端渲染html卡片服務的例子,使用vue-ssr加express實作服務

什麼是伺服器端渲染(SSR)

Vue.js 是建構用戶端應用程式的架構。預設情況下,可以在浏覽器中輸出 Vue 元件,進行生成 DOM 和操作 DOM。然而,也可以将同一個元件渲染為伺服器端的 HTML 字元串,将它們直接發送到浏覽器,最後将這些靜态标記"激活"為用戶端上完全可互動的應用程式。

搭建vue工程

利用 webpack 搭建一個簡單的 vue 開發環境

結構如下:

使用Vue-ssr提供服務端渲染html卡片服務什麼是伺服器端渲染(SSR)搭建vue工程實作服務端渲染編寫服務端渲染的卡片模闆

router 和 store 以及 vue 都采用了工廠函數來生成執行個體,這是為了友善代碼在後面的服務端渲染中進行複用,因為 “Node.js 伺服器是一個長期運作的程序。必須為每個請求建立一個新的 Vue 執行個體”

app.js:

import Vue from 'vue'
import App from './App.vue'
import { createRouter } from './routes'
import { createStore } from './stores'
import { sync } from 'vuex-router-sync'
// 導出一個工廠函數,用于建立新的
// 應用程式、router 和 store 執行個體
export function createApp () {
  // 建立 router 和 store 執行個體
  const router = createRouter()
  const store = createStore()

  // 同步路由狀态(route state)到 store
  sync(store, router)

  // 建立應用程式執行個體,将 router 和 store 注入
  const app = new Vue({
    router,
    store,
    render: h => h(App)
  })
  return { router, app, store }
  // return { app }
}
           

實作服務端渲染

安裝:

npm install vue vue-server-renderer --save

實作服務端渲染,需增加如下 webpack 配置:

webpack.server.conf.js:

const path = require('path')
const webpack = require('webpack')
const merge = require('webpack-merge')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const VueSSRServerPlugin = require('vue-server-renderer/server-plugin')

const baseWebpackConfig = require('./webpack.client.conf')
const config = require('./config')

module.exports = merge(baseWebpackConfig, {
  entry: './src/entry-server.js',
  // 告知 `vue-loader` 輸送面向伺服器代碼(server-oriented code)。
  target: 'node',
  output: {
    filename: 'server-bundle.js',
    libraryTarget: 'commonjs2',
  },
  plugins: [
     new VueSSRServerPlugin()
  ]
})
           

為服務端渲染準備的入口檔案:

entry-server.js:

import { createApp } from './app'
// 這裡的 context 是服務端渲染模闆時傳入的
export default context => {
  // 因為有可能會是異步路由鈎子函數或元件,是以我們将傳回一個 Promise,
  // 以便伺服器能夠等待所有的内容在渲染前,
  // 就已經準備就緒。
  return new Promise((resolve, reject) => {
    const { app, router, store } = createApp()

    const { url } = context
    const { fullPath } = router.resolve(url).route

    if (fullPath !== url) {
      return reject({ url: fullPath })
    }

    router.push(url)

    // 等到 router 将可能的異步元件和鈎子函數解析完
    router.onReady(() => {
      const matchedComponents = router.getMatchedComponents()
      // 比對不到的路由,執行 reject 函數,并傳回 404
      if (!matchedComponents.length) {
        return reject({ code: 404 })
      }

      // 執行所有元件中的異步資料請求
      Promise.all(matchedComponents.map(({ asyncData }) => asyncData && asyncData({
        store,
        route: router.currentRoute
      }))).then(() => {
        // 在所有預取鈎子(preFetch hook) resolve 後,
        // 我們的 store 現在已經填充入渲染應用程式所需的狀态。
        // 當我們将狀态附加到上下文,
        // 并且 `template` 選項用于 renderer 時,
        // 狀态将自動序列化為 `window.__INITIAL_STATE__`,并注入 HTML。
        context.state = store.state
        resolve(app)
      }).catch(reject)
    }, reject)
  })
}
           

編譯一下,運作 npm run build:server ,将會在 dist 目錄下得到 vue-ssr-server-bundle.json 檔案。該檔案包含了 webpack 打包生成的所有 chunk 并指定了入口。後面服務端會基于該檔案來做渲染。

編寫服務端渲染代碼,使用express做服務容器:

server.js:

const Vue = require('vue')
 const express = require('express')
 const bodyParser = require('body-parser')
 const fs = require('fs')
 const path = require('path')
 const { createBundleRenderer } = require('vue-server-renderer')
 const bundle = require('./dist/vue-ssr-server-bundle.json')
 const server = express()

 const renderer = createBundleRenderer(bundle, {
   template: fs.readFileSync('./index.template.html', 'utf-8')
 })

// 這個方法傳回一個僅僅用來解析json格式的中間件。這個中間件能接受任何body中任何Unicode編碼的字元。支援自動的解析gzip和 zlib。
server.use(bodyParser.json());
// 這個方法也傳回一個中間件,這個中間件用來解析body中的urlencoded字元,隻支援utf-8的編碼的字元。同樣也支援自動的解析gzip和 zlib。
server.use(bodyParser.urlencoded({ extended: false }));

// 服務端渲染
server.get('*', (req, res) => {
  const context = { url: req.originalUrl }
  renderer.renderToString(context, (err, html) => {
    if (err) {
      if (err.code === 404) {
        res.status(404).end('Page not found')
      } else {
        res.status(500).end('Internal Server Error')
      }
    } else {
      res.end(html)
    }
  })
})

server.listen(8081)
           

使用上面生成的檔案建立了一個 renderer 對象,然後調用其 renderToString 方法并傳入包含請求路徑的對象作為參數來進行渲染,最後将渲染好的資料即 html 傳回。

編寫服務端渲染的卡片模闆

robot_baike.vue

<template>
    <div>
        <span>
            <p>{{data}}</p>
        </span>
        <div id="aaa" ></div>
        <div v-html="bbb"></div>

    </div>
</template>
<script>

    let charts = [];

    export default {
        data(){
            return {
                data:null,
                question:null,
                bbb:""
            };
        },
        methods: {

        },
        created() {
            this.data = "馬雲";
            charts = "[129.9, 171.5, 306.4, 429.2, 144.0, 176.0, 135.6, 248.5, 216.4, 194.1, 95.6, 54.4]";
            this.bbb = "<script>setTimeout(function (){let chart = Highcharts.chart('aaa', {'chart':{'type':'column',},'series': [{'data':"+charts+"}]});},100);<\/script>";
        },
    }
</script>
<style >

</style>
           

文本内容可處理後直接渲染到頁面節點,但圖形js插件生成的圖形則需要在用戶端進行渲染。

可以将繪圖代碼放入一個 < script> 标簽,拼裝為字元串,用v-html拼入頁面中,則在用戶端會執行此繪圖代碼生成圖形。

最後配置下路由:

routes/inex.js

import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
export function createRouter () {
  return new Router({
    mode: 'history',
    routes: [
      { path: '/baike', component: () => import('COMPONENTS/robot_baike') }
    ]
  })
}
           

運作 npm run server 啟動服務端,打開 http://localhost:8081/baike 就可以看到效果了。

使用Vue-ssr提供服務端渲染html卡片服務什麼是伺服器端渲染(SSR)搭建vue工程實作服務端渲染編寫服務端渲染的卡片模闆