似乎webpack-dev-server是在假定你只有一个公共入口点的情况下构建的(即“ / ”),而react-router允许使用无限数量的入口点。
我想要利用webpack-dev-server带来的好处,尤其是它对提高生产力非常有用的热重载功能,但我仍然希望能够加载在react-router中设置的路由。
如何实现它们一起工作?是否可以以某种方式在webpack-dev-server前面运行express服务器以允许这样做?
要使此功能正常工作,您应将WebpackDevServer
的historyApiFallback
设置为true。以下是一个小例子(根据需要进行调整):
var webpack = require('webpack');
var WebpackDevServer = require('webpack-dev-server');
var config = require('./webpack.config');
var port = 4000;
var ip = '0.0.0.0';
new WebpackDevServer(webpack(config), {
publicPath: config.output.publicPath,
historyApiFallback: true,
}).listen(port, ip, function (err) {
if(err) {
return console.log(err);
}
console.log('Listening at ' + ip + ':' + port);
});
true
,那么可以这样做。 - Juho Vepsäläinenwebpack-dev-server --history-api-fallback
。 - Levi我设置了一个代理来实现这个目的:
你有一个正则表达式web服务器,它可以在任何路由上提供index.html文件,但是如果它是资源路由,则请求将被代理到web-dev-server。
你的React热更新入口仍将直接指向webpack dev server,因此热重载仍然有效。
假设你在8081上运行webpack-dev-server,代理在8080上。你的server.js文件将如下所示:
"use strict";
var webpack = require('webpack');
var WebpackDevServer = require('webpack-dev-server');
var config = require('./make-webpack-config')('dev');
var express = require('express');
var proxy = require('proxy-middleware');
var url = require('url');
## --------your proxy----------------------
var app = express();
## proxy the request for static assets
app.use('/assets', proxy(url.parse('http://localhost:8081/assets')));
app.get('/*', function(req, res) {
res.sendFile(__dirname + '/index.html');
});
# -----your-webpack-dev-server------------------
var server = new WebpackDevServer(webpack(config), {
contentBase: __dirname,
hot: true,
quiet: false,
noInfo: false,
publicPath: "/assets/",
stats: { colors: true }
});
## run the two servers
server.listen(8081, "localhost", function() {});
app.listen(8080);
现在您可以按以下方式在webpack配置文件中创建入口点:
entry: [
'./src/main.js',
'webpack/hot/dev-server',
'webpack-dev-server/client?http://localhost:8081'
]
注意热更新的直接调用是8081。
同时确保将绝对URL传递给output.publicPath
选项:
output: {
publicPath: "http://localhost:8081/assets/",
// ...
}
output.publicPath
选项的注释,它也应该是绝对 URL。 - Tobias K.historyApiFallback
。 - Eugene Kulabuhov对于其他可能仍在寻找答案的人,我提供了一个简单的代理绕过方法,可以轻松地实现此操作,并且配置将放入webpack.config.js中。
我确定使用正则表达式测试本地内容有更加优雅的方式,但这适合我的需求。
devServer: {
proxy: {
'/**': { //catch all requests
target: '/index.html', //default target
secure: false,
bypass: function(req, res, opt){
//your custom code to check for any exceptions
//console.log('bypass check', {req: req, res:res, opt: opt});
if(req.path.indexOf('/img/') !== -1 || req.path.indexOf('/public/') !== -1){
return '/'
}
if (req.headers.accept.indexOf('html') !== -1) {
return '/index.html';
}
}
}
}
}
如果你使用CLI运行webpack-dev-server,你可以通过webpack.config.js配置devServer对象,并使用historyApiFallback选项:
module.exports = {
entry: "index.js",
output: {
filename: "bundle.js"
},
devServer: {
historyApiFallback: true
}
}
每次出现404错误时,都会将其重定向到index.html。
提示:如果使用publicPath,则还需要将其传递给devServer:
module.exports = {
entry: "index.js",
output: {
filename: "bundle.js",
publicPath: "admin/dashboard"
},
devServer: {
historyApiFallback: {
index: "admin/dashboard"
}
}
}
对于更近期的回答,当前版本的webpack(4.1.1),您可以在webpack.config.js中像这样设置:
const webpack = require('webpack');
module.exports = {
entry: [
'react-hot-loader/patch',
'./src/index.js'
],
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: ['babel-loader']
},
{
test: /\.css$/,
exclude: /node_modules/,
use: ['style-loader','css-loader']
}
]
},
resolve: {
extensions: ['*', '.js', '.jsx']
},
output: {
path: __dirname + '/dist',
publicPath: '/',
filename: 'bundle.js'
},
plugins: [
new webpack.HotModuleReplacementPlugin()
],
devServer: {
contentBase: './dist',
hot: true,
historyApiFallback: true
}
};
重要的部分是 historyApiFallback: true
。无需运行自定义服务器,只需使用cli:
"scripts": {
"start": "webpack-dev-server --config ./webpack.config.js --mode development"
},
针对运行同构应用程序(即服务器端渲染React组件)的情况,我想补充一下答案。
在这种情况下,您可能还希望在更改React组件之一时自动重新加载服务器。您可以使用piping
包来完成这个操作。您只需安装它并在server.js的开头添加require("piping")({hook: true})
即可。就是这样。当您更改任何组件时,服务器将重新启动。
然而,这会引起另一个问题 - 如果您从与express服务器相同的进程中运行webpack服务器(如上面接受的答案),则webpack服务器也将重新启动,并且每次都会重新编译您的捆绑包。为避免这种情况,您应该在不同的进程中运行主服务器和webpack服务器,以便piping只重新启动您的express服务器并且不会触及webpack。
您可以使用concurrently
包来完成此操作。您可以在react-isomorphic-starterkit中找到此示例。在package.json中,他有:
"scripts": {
...
"watch": "node ./node_modules/concurrently/src/main.js --kill-others 'npm run watch-client' 'npm run start'"
},
同时运行两个服务器,但是在不同的进程中。
historyApiFallback
也可以是一个对象而不是布尔值,其中包含路由信息。
historyApiFallback: navData && {
rewrites: [
{ from: /route-1-regex/, to: 'route-1-example.html' }
]
}
也许不是所有情况都适用,但在devServer中使用publicPath: '/'
选项似乎是解决深度路由问题最简单的方法,请参见:https://github.com/ReactTraining/react-router/issues/676
app.get('*'...
index.html解析器,因此express将首先检查请求是否与webpack提供的路由之一匹配(例如:/dist/bundle.js
或/__webpack_hmr_
),如果不是,则会移动到带有*
解析器的index.html
。app.use(require('webpack-dev-middleware')(compiler, {
publicPath: webpackConfig.output.publicPath,
}))
app.use(require('webpack-hot-middleware')(compiler))
app.get('*', function(req, res) {
sendSomeHtml(res)
})