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