我尝试使用webpack设置自己的快速服务器以使用热模块更换&服务器端渲染。除了需要快速中间件函数和webpack-hot-server-middleware
参数的(res, req, next)
的集成之外,一切似乎都有效 - 但我无法理解如何正确实现它。
到目前为止,这是我的配置:
webpack.config.js
const webpack = require('webpack');
const path = require('path');
const config = [{
name: 'client',
entry: [
'webpack-hot-middleware/client',
'./src/js/entry_client.js'
],
output: {
path: path.resolve(__dirname, 'dist/js/'),
filename: 'app.js'
},
module: {
rules: [{
test: /\.vue$/,
loader: 'vue-loader'
}, {
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader'
}]
},
resolve: {
alias: {
vue$: 'vue/dist/vue.runtime.js'
}
}
}, {
name: 'server',
target: 'node',
entry: './src/js/entry_server.js',
output: {
path: path.resolve(__dirname, 'dist/js/'),
filename: 'app.server.js',
libraryTarget: 'commonjs2'
},
externals: require('webpack-node-externals')(),
module: {
rules: [{
test: /\.vue$/,
loader: 'vue-loader'
}]
},
resolve: {
alias: {
vue$: 'vue/dist/vue.runtime.js',
}
}
}];
if (process.env.NODE_ENV !== 'production') {
config[0].plugins = [new webpack.HotModuleReplacementPlugin()];
}
module.exports = config;
entry_client.js
import {createApp} from './app';
const {app, router} = createApp();
router.onReady(() => {
app.$mount('#app');
});
entry_server.js
import {createApp} from './app';
export default context => {
return new Promise((resolve, reject) => {
const {app, router} = createApp();
router.push(context.url);
router.onReady(() => {
if (!router.getMatchedComponents().length) return reject({code: 404});
resolve(app);
}, reject);
});
};
app.js
import Vue from 'vue';
import App from '../vue/app.vue';
import {createRouter} from './router';
export function createApp() {
const router = createRouter();
const app = new Vue({
router,
render: h => h(App)
});
return {app, router};
}
router.js
import Vue from 'vue';
import Router from 'vue-router';
import Home from '../vue/home.vue';
import About from '../vue/about.vue';
Vue.use(Router);
export function createRouter() {
return new Router({
mode: 'history',
routes: [{
path: '/',
component: Home
}, {
path: '/about',
component: About
}]
});
}
server.js
const express = require('express');
const fs = require('fs');
const path = require('path');
const bundle = require('./dist/js/app.server');
const renderer = require('vue-server-renderer').createRenderer({
template: fs.readFileSync('./src/html/index.html', 'utf-8')
});
const server = express();
// add vue HMR middleware
if (process.env.NODE_ENV !== 'production') {
const webpack = require('webpack');
const compiler = webpack(require('./webpack.config'));
server.use(require('webpack-dev-middleware')(compiler, {
noInfo: true,
serverSideRender: true
}));
server.use(require('webpack-hot-middleware')(compiler.compilers.find(compiler => compiler.name === 'client')));
// server.use(require('webpack-hot-server-middleware')(compiler));
} else {
// static file serving
server.use(require('serve-favicon')(path.join(__dirname, 'src/png/favicon-32x32.png')));
server.use(express.static(path.join(__dirname, 'dist/'), {index: false}));
}
server.get('*', (req, res) => {
bundle.default({url: req.url}).then(app => {
renderer.renderToString(app, {
title: 'Some title',
description: 'Some description'
}, (err, html) => {
if (err) {
console.error('Error in renderToString:', err);
return res.status(500).send('Internal Server Error');
}
res.send(html);
});
}, err => {
if (err.code === 404) return res.status(404).send('Page not found');
console.error(err);
return res.status(500).send('Internal Server Error');
});
});
server.listen(4040);