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
南順網絡








