您當前位置: 南順網絡>> 官方資訊>> 建站知識

Vue 2.0 服務端怎麽渲染

// 步驟 1:創建一(yī)個Vue實例 var Vue = require('vue') var app = new Vue({  render: function (h) {    return h('p', 'hello world')  } }) // 步驟 2: 創建一(yī)個渲染器 var renderer = require('vue-server-renderer').createRenderer() // 步驟 3: 将 Vue實例 渲染成 HTML renderer.renderToString(app, function (error, html) {  if (error) throw error  console.log(html)  // => <p server-rendered="true">hello world</p> })

這樣子(zǐ),配合通常的(de) Node 服務端框架就可(kě)以簡單來實現服務端渲染了,可(kě)是,在真實場景中,我們一(yī)般采用 .vue 文件的(de)模塊組織方式,這樣的(de)話,服務端渲染就需要使用 webpack 來将 Vue 組件進行打包為(wèi)單個文件。

2. 配合 Webpack 渲染 .vue 文件

先建立一(yī)個服務端的(de)入口文件 server.js

import Vue from 'vue';

import App from './vue/App';

export default function (options) {
    const VueApp = Vue.extend(App);

    const app = new VueApp(Object.assign({}, options));

    return new Promise(resolve => {
        resolve(app);
    });
}

這裏和(hé)浏覽器端的(de)入口文件大同小異,隻是默認導出了一(yī)個函數,這個函數接收一(yī)個服務端渲染時服務端傳入的(de)一(yī)些配置,返回一(yī)個包含了 app 實例的(de) Promise;

簡單寫一(yī)個 App.vue 的(de)文件

<template>
    <h1>{{ title }}</h1>
</template>

<script>
module.exports = {
    props: ['title']
</script>

這裏将會讀取服務端入口文件傳入 options 的(de) data 屬性,取到 title 值,渲染到對應 DOM 中;

再看看 Webpack 的(de)配置,和(hé)客戶端渲染同樣是大同小異:

const webpack = require('webpack');
const path = require('path');
const projectRoot = __dirname;

const env = process.env.NODE_ENV || 'development';

module.exports = {
    target: 'node', // 告訴 Webpack 是 node 代碼的(de)打包
    devtool: null, // 既然是 node 就不用 devtool 了
    entry: {
        app: path.join(projectRoot, 'src/server.js')
    },
    output: Object.assign({}, base.output, {
        path: path.join(projectRoot, 'src'),
        filename: 'bundle.server.js',
        libraryTarget: 'commonjs2' // 和(hé)客戶端不同
    }),
    plugins: [
        new webpack.DefinePlugin({
            'process.env.NODE_ENV': JSON.stringify(env),
            'process.env.VUE_ENV': '"server"' // 配置 vue 的(de)環境變量,告訴 vue 是服務端渲染,就不會做(zuò)耗性能的(de) dom-diff 操作了
        })
    ],
    resolve: {
        extensions: ['', '.js', '.vue'],
        fallback: [path.join(projectRoot, 'node_modules')]
    },
    resolveLoader: {
        root: path.join(projectRoot, 'node_modules')
    },
    module: {
        loaders: [
            {
                test: /\.vue$/,
                loader: 'vue'
            },
            {
                test: /\.js$/,
                loader: 'babel',
                include: projectRoot,
                exclude: /node_modules/
            }
        ]
    }
};

其中主要就是三處不同:聲明 node 模塊打包;修改打包後模塊加載方式為(wèi) commonjs(commonjs2 具體可(kě)以看 Webpack 官方文檔);再就是 vue 的(de)服務端打包優化了,這部分如(rú)果不傳的(de)話後面 vue 服務端渲染會慢至幾十秒,一(yī)度以為(wèi)服務端代碼挂了。

最後就是服務端載入生成的(de) bundle.server.js 文件:

const fs = require('fs');
const path = require('path');
const vueServerRenderer = require('vue-server-renderer');
const filePath = path.join(__dirname, 'src/bundle.server.js');

// 讀取 bundle 文件,并創建渲染器
const code = fs.readFileSync(filePath, 'utf8');
const bundleRenderer = vueServerRenderer.createBundleRenderer(code);

// 渲染 Vue 應用為(wèi)一(yī)個字符串
bundleRenderer.renderToString(options, (err, html) => {
    if (err) {
        console.error(err);
    }

    content.replace('<div id="app"></div>', html);
});

這裏 options 可(kě)以傳入 vue 組件所需要的(de) data 等信息;下面還是以官方實例中的(de) express 來做(zuò)服務端示例下:

const fs = require('fs');
const path = require('path');
const vueServerRenderer = require('vue-server-renderer');
const filePath = path.join(think.ROOT_PATH, 'view/bundle.server.js');
global.Vue = require('vue')

// 讀取 bundle 文件,并創建渲染器
const code = fs.readFileSync(filePath, 'utf8');
const bundleRenderer = vueServerRenderer.createBundleRenderer(code);

// 創建一(yī)個Express服務器
var express = require('express');
var server = express();

// 部署靜态文件夾為(wèi) "assets" 文件夾
server.use('/assets', express.static(
    path.resolve(__dirname, 'assets');
));

// 處理(lǐ)所有的(de) Get 請求
server.get('*', function (request, response) {
    // 設置一(yī)些數據,可(kě)以是數據庫讀取等等
    const options = {
        data: {
            title: 'hello world'
        }
    };

    // 渲染 Vue 應用為(wèi)一(yī)個字符串
    bundleRenderer.renderToString(options, (err, html) => {
        // 如(rú)果渲染時發生了錯誤
        if (err) {
            // 打印錯誤到控制台
            console.error(err);
            // 告訴客戶端錯誤
            return response.status(500).send('Server Error');
        }

        // 發送布局和(hé)HTML文件
        response.send(layout.replace('<div id="app"></div>', html));
    });

// 監聽5000端口
server.listen(5000, function (error) {
    if (error) throw error
    console.log('Server is running at localhost:5000')
});

這樣子(zǐ)基本就是 Vue 服務端渲染的(de)整個流程了


編輯:--ns868