您当前位置: 南顺网络>> 官方资讯>> 建站知识

Vue 2.0 服务端怎么渲染

// 步骤 1:创建一个Vue实例 var Vue = require('vue') var app = new Vue({  render: function (h) {    return h('p', 'hello world')  } }) // 步骤 2: 创建一个渲染器 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> })

这样子,配合通常的 Node 服务端框架就可以简单来实现服务端渲染了,可是,在真实场景中,我们一般采用 .vue 文件的模块组织方式,这样的话,服务端渲染就需要使用 webpack 来将 Vue 组件进行打包为单个文件。

2. 配合 Webpack 渲染 .vue 文件

先建立一个服务端的入口文件 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);
    });
}

这里和浏览器端的入口文件大同小异,只是默认导出了一个函数,这个函数接收一个服务端渲染时服务端传入的一些配置,返回一个包含了 app 实例的 Promise;

简单写一个 App.vue 的文件

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

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

这里将会读取服务端入口文件传入 options 的 data 属性,取到 title 值,渲染到对应 DOM 中;

再看看 Webpack 的配置,和客户端渲染同样是大同小异:

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

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

module.exports = {
    target: 'node', // 告诉 Webpack 是 node 代码的打包
    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' // 和客户端不同
    }),
    plugins: [
        new webpack.DefinePlugin({
            'process.env.NODE_ENV': JSON.stringify(env),
            'process.env.VUE_ENV': '"server"' // 配置 vue 的环境变量,告诉 vue 是服务端渲染,就不会做耗性能的 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 模块打包;修改打包后模块加载方式为 commonjs(commonjs2 具体可以看 Webpack 官方文档);再就是 vue 的服务端打包优化了,这部分如果不传的话后面 vue 服务端渲染会慢至几十秒,一度以为服务端代码挂了。

最后就是服务端载入生成的 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 应用为一个字符串
bundleRenderer.renderToString(options, (err, html) => {
    if (err) {
        console.error(err);
    }

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

这里 options 可以传入 vue 组件所需要的 data 等信息;下面还是以官方实例中的 express 来做服务端示例下:

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);

// 创建一个Express服务器
var express = require('express');
var server = express();

// 部署静态文件夹为 "assets" 文件夹
server.use('/assets', express.static(
    path.resolve(__dirname, 'assets');
));

// 处理所有的 Get 请求
server.get('*', function (request, response) {
    // 设置一些数据,可以是数据库读取等等
    const options = {
        data: {
            title: 'hello world'
        }
    };

    // 渲染 Vue 应用为一个字符串
    bundleRenderer.renderToString(options, (err, html) => {
        // 如果渲染时发生了错误
        if (err) {
            // 打印错误到控制台
            console.error(err);
            // 告诉客户端错误
            return response.status(500).send('Server Error');
        }

        // 发送布局和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')
});

这样子基本就是 Vue 服务端渲染的整个流程了


编辑:--ns868