web-dev-qa-db-fra.com

Comment autoriser Webpack-dev-server à autoriser les points d'entrée de react-router

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?

109
Nathan Wienert

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/",
        // ...
    }
68
Retozi

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);
});
100
Juho Vepsäläinen

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';
        }
      }
    }
  }
} 
24
Werner Weber

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 ").

enter image description here

10
Eugene Kulabuhov

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"
  },
8
Michael Brown

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.

2
Viacheslav

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' }
  ]
}
1
Tom Roggero

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

1
Alex

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)
})
0
Graham Norton