Je crée une application qui utilise Webpack-dev-server en développement aux côtés de react-router.
Il semble que webpack-dev-server soit basé sur l'hypothèse que vous aurez un point d'entrée public à un endroit (c'est-à-dire "/"), alors que react-router autorise un nombre illimité de points d'entrée.
Je souhaite bénéficier des avantages du serveur webpack-dev-server, en particulier de la fonctionnalité de rechargement à chaud, gage de productivité, mais je souhaite tout de même pouvoir charger les itinéraires définis dans react-router.
Comment peut-on l'implémenter de telle sorte qu'ils travaillent ensemble? Pourriez-vous exécuter un serveur express devant webpack-dev-server de manière à permettre cela?
J'ai mis en place un proxy pour y parvenir:
Vous avez un serveur Web Express standard qui sert le fichier index.html sur n’importe quel itinéraire, sauf s’il s’agit d’un itinéraire d’actifs. s'il s'agit d'un actif, la demande est envoyée par proxy au serveur web-dev
vos entrées chaudes réactives seront toujours dirigées directement vers le serveur de développement Webpack, de sorte que le rechargement à chaud fonctionne toujours.
Supposons que vous exécutiez webpack-dev-server sur 8081 et votre proxy à 8080. Votre fichier server.js ressemblera à ceci:
"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);
faites maintenant vos points d’entrée dans la configuration du webpack comme ceci:
entry: [
'./src/main.js',
'webpack/hot/dev-server',
'webpack-dev-server/client?http://localhost:8081'
]
notez l'appel direct au 8081 pour hotreload
assurez-vous également que vous transmettez une URL absolue à l'option output.publicPath
:
output: {
publicPath: "http://localhost:8081/assets/",
// ...
}
Pour que cela fonctionne, vous devez définir historyApiFallback
sur WebpackDevServer
. Voici un petit exemple (Tweak pour répondre à vos besoins):
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);
});
Pour tous ceux qui cherchent encore cette réponse. J'ai mis en place un contournement simple de proxy qui le fait sans trop de soucis et la config va dans le webpack.config.js
Je suis sûr qu'il existe des moyens beaucoup plus élégants de tester le contenu local à l'aide de regex, mais cela répond à mes besoins.
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';
}
}
}
}
}
Si vous exécutez webpack-dev-server à l'aide de la CLI, vous pouvez le configurer via webpack.config.js en transmettant l'objet devServer:
module.exports = {
entry: "index.js",
output: {
filename: "bundle.js"
},
devServer: {
historyApiFallback: true
}
}
Cela redirigera vers index.html à chaque fois qu'il est rencontré.
REMARQUE: Si vous utilisez publicPath, vous devrez également le transmettre à devServer:
module.exports = {
entry: "index.js",
output: {
filename: "bundle.js",
publicPath: "admin/dashboard"
},
devServer: {
historyApiFallback: {
index: "admin/dashboard"
}
}
}
Vous pouvez vérifier que tout est correctement configuré en consultant les premières lignes de la sortie (la partie contenant "404s se repliera sur: chemin ").
Pour une réponse plus récente, la version actuelle de webpack (4.1.1) vous pouvez simplement définir ceci dans votre webpack.config.js comme ceci:
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
}
};
La partie importante est historyApiFallback: true
. Pas besoin de faire tourner un serveur personnalisé, utilisez simplement la clé:
"scripts": {
"start": "webpack-dev-server --config ./webpack.config.js --mode development"
},
J'aimerais ajouter quelque chose à la réponse lorsque vous exécutez une application isomorphe (c'est-à-dire que vous rendez le composant React côté serveur.)
Dans ce cas, vous souhaiterez probablement également recharger automatiquement le serveur lorsque vous modifierez l'un de vos composants React. Vous faites cela avec le package piping
. Tout ce que vous avez à faire est de l'installer et d'ajouter require("piping")({hook: true})
quelque part au début de vous server.js . C'est ça. Le serveur redémarrera après avoir modifié tout composant utilisé par celui-ci.
Cela pose cependant un autre problème: si vous exécutez le serveur Webpack à partir du même processus que votre serveur express (comme dans la réponse acceptée ci-dessus), le serveur Webpack redémarrera également et recompilera votre paquet à chaque fois. Pour éviter cela, vous devez exécuter votre serveur principal et votre serveur Webpack dans différents processus afin que la canalisation ne redémarre que votre serveur express et ne touche pas Webpack. Vous pouvez le faire avec le package concurrently
. Vous pouvez trouver un exemple de cela dans react-isomorphic-starterkit . Dans le fichier package.json , il a:
"scripts": {
...
"watch": "node ./node_modules/concurrently/src/main.js --kill-others 'npm run watch-client' 'npm run start'"
},
qui exécute les deux serveurs simultanément mais dans des processus séparés.
historyApiFallback
peut également être un objet au lieu d'un booléen, contenant les itinéraires.
historyApiFallback: navData && {
rewrites: [
{ from: /route-1-regex/, to: 'route-1-example.html' }
]
}
Peut-être pas dans tous les cas, mais il semble que l'option publicPath: '/'
dans le devServer soit la solution la plus facile pour résoudre le problème des routes profondes, voir: https://github.com/ReactTraining/react-router/issues/ 676
Cela a fonctionné pour moi: il suffit d’ajouter d’abord les middlewares Webpack et le résolveur app.get('*'...
index.html plus tard,
ainsi, express va d'abord vérifier si la demande correspond à l'une des routes fournies par le webpack (comme: /dist/bundle.js
ou /__webpack_hmr_
) et si ce n'est pas le cas, elle sera déplacée vers le index.html
avec le *
résolveur.
c'est à dire:
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)
})