Come consentire a webpack-dev-server di consentire i punti di ingresso da React-Router


117

Sto creando un'app che utilizza webpack-dev-server in fase di sviluppo insieme a react-router.

Sembra che webpack-dev-server sia costruito intorno al presupposto che si avrà un punto di ingresso pubblico in un punto (es. "/"), Mentre reatt-router consente un numero illimitato di punti di ingresso.

Voglio i vantaggi del webpack-dev-server, in particolare la funzione di ricarica a caldo che è ottima per la produttività, ma voglio comunque essere in grado di caricare le rotte impostate in react-router.

Come si potrebbe implementarlo in modo che lavorino insieme? Potresti eseguire un server espresso davanti a webpack-dev-server in modo tale da consentirlo?


Ho una versione estremamente hacky di qualcosa qui, ma è fragile e consente solo la corrispondenza di percorsi semplici: github.com/natew/react-base (vedi make-webpack-config) e (app / routes.js)
Nathan Wienert

Sei riuscito a risolvere questo problema Nathan? Se é cosi, come? Per favore, prova a rispondere alla mia domanda qui stackoverflow.com/questions/31091702/… . Grazie..!
SudoPlz

Risposte:


69

Ho impostato un proxy per ottenere questo:

Hai un server web espresso regolare che serve index.html su qualsiasi percorso, tranne se si tratta di un percorso di risorse. se si tratta di una risorsa, la richiesta viene inviata tramite proxy al web-dev-server

i punti di ingresso a caldo di React punteranno ancora direttamente al server di sviluppo webpack, quindi il ricaricamento a caldo funziona ancora.

Supponiamo che tu esegua webpack-dev-server su 8081 e il tuo proxy su 8080. Il tuo file server.js avrà questo aspetto:

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

ora crea i tuoi punti di ingresso nella configurazione del webpack in questo modo:

 entry: [
     './src/main.js',
     'webpack/hot/dev-server',
     'webpack-dev-server/client?http://localhost:8081'
 ]

notare la chiamata diretta al 8081 per hotreload

assicurati anche di passare un URL assoluto output.publicPathall'opzione:

 output: {
     publicPath: "http://localhost:8081/assets/",
     // ...
 }

1
Ehi, è fantastico. In realtà sono arrivato a questa configurazione poco prima e stavo per pubblicare una risposta, ma penso che tu abbia fatto un lavoro migliore.
Nathan Wienert

1
Una domanda, in qualche modo non correlata, quindi posso aprire una nuova domanda se necessario, ma ho notato che ora l'output della console dal server di sviluppo webpack non viene trasmesso in streaming. Prima potevi vederlo compilare e vedere aumentare le percentuali, ora blocca solo gli output dopo la compilazione.
Nathan Wienert

Ben fatto. Questo è esattamente come dovrebbe essere fatto. Ho aggiunto una nota output.publicPathsull'opzione, che dovrebbe essere anche un URL assoluto.
Tobias K.

5
Sarebbe più semplice utilizzare invece un proxy webpack integrato . Così non interferisci nel server stesso, lasci il server puro . Invece, fai solo una piccola aggiunta (3-5 righe) a webpack config. Grazie a ciò modifichi solo gli script di sviluppo per scopi di sviluppo e lasci il codice di produzione (server.js) in pace (a differenza della tua versione) e imo è il modo corretto di procedere.
jalooc

3
Questa risposta è ancora corretta anche se un po 'datata. Ora sono disponibili modi più semplici, cerca historyApiFallback.
Eugene Kulabuhov

102

È necessario impostare historyApiFallbacksu WebpackDevServercome vero per questo al lavoro. Ecco un piccolo esempio (modifica per adattarla ai tuoi scopi):

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);
});

Ti mancherà la barra di stato nella parte superiore del tuo index.html, ma funziona benissimo :)
swennemen

7
Questa dovrebbe essere la risposta accettata. Dai documenti del server webpack dev: "Se stai utilizzando l'API della cronologia HTML5, probabilmente devi fornire il tuo index.html al posto delle risposte 404, operazione che può essere eseguita impostando historyApiFallback: true" Se capisco correttamente la domanda, questo risolverà il problema.
Sebastian

così semplice ... Grazie!
smnbbrv

1
@smnbbrv Nessun problema. In realtà utilizza connect-history-api-fallback sotto e puoi passare un oggetto con le opzioni specifiche del middleware se vuoi invece di solo true.
Juho Vepsäläinen

1
OPPURE se stai usando il cli,webpack-dev-server --history-api-fallback
Levi

27

Per chiunque altro stia ancora cercando questa risposta. Ho messo insieme un semplice bypass proxy che ottiene questo risultato senza troppi problemi e la configurazione va in webpack.config.js

Sono sicuro che ci sono modi molto più eleganti per testare il contenuto locale usando regex, ma funziona per le mie esigenze.

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';
        }
      }
    }
  }
} 

Ha funzionato bene per me
Nath

Ha funzionato bene! .. Grazie!
Dhrumil Bhankhar

Questa è solo una risposta perfetta, facile e veloce.
domino

12

Se stai eseguendo webpack-dev-server utilizzando la CLI, puoi configurarlo tramite webpack.config.js passando l'oggetto devServer:

module.exports = {
  entry: "index.js",
  output: {
    filename: "bundle.js"
  },
  devServer: {
    historyApiFallback: true
  }
}

Questo reindirizzerà a index.html ogni volta che viene incontrato 404.

NOTA: se stai usando publicPath, dovrai passarlo anche a devServer:

module.exports = {
  entry: "index.js",
  output: {
    filename: "bundle.js",
    publicPath: "admin/dashboard"
  },
  devServer: {
    historyApiFallback: {
      index: "admin/dashboard"
    }
  }
}

Puoi verificare che tutto sia impostato correttamente guardando le prime righe dell'output (la parte con "404s farà il fallback a: path ").

inserisci qui la descrizione dell'immagine


11

Per una risposta più recente, la versione corrente di webpack (4.1.1) puoi semplicemente impostarla nel tuo webpack.config.js in questo modo:

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 parte importante è historyApiFallback: true. Non è necessario eseguire un server personalizzato, basta usare il cli:

"scripts": {
    "start": "webpack-dev-server --config ./webpack.config.js --mode development"
  },

2

Vorrei aggiungere alla risposta per il caso in cui si esegue un'app isomorfa (ovvero il rendering del componente React lato server).

In questo caso probabilmente vorrai anche ricaricare automaticamente il server quando cambi uno dei tuoi componenti React. Lo fai con il pipingpacchetto. Tutto quello che devi fare è installarlo e aggiungere require("piping")({hook: true})da qualche parte all'inizio del tuo server.js . Questo è tutto. Il server verrà riavviato dopo aver modificato qualsiasi componente da esso utilizzato.

Tuttavia, questo solleva un altro problema: se esegui il server webpack dallo stesso processo del tuo server Express (come nella risposta accettata sopra), anche il server webpack si riavvierà e ricompilerà il tuo pacchetto ogni volta. Per evitare ciò, è necessario eseguire il server principale e il server webpack in processi diversi in modo che il piping riavvii solo il server Express e non tocchi il webpack. Puoi farlo con concurrentlypackage. È possibile trovare un esempio di ciò nel kit di avvio per reattività isomorfica . Nel package.json ha:

"scripts": {
    ...
    "watch": "node ./node_modules/concurrently/src/main.js --kill-others 'npm run watch-client' 'npm run start'"
  },

che esegue entrambi i server contemporaneamente ma in processi separati.


Significa che alcuni file vengono guardati due volte? Come i file isomorfi / universali condivisi?
David Sinclair

1

historyApiFallback può anche essere un oggetto invece di un booleano, contenente le rotte.

historyApiFallback: navData && {
  rewrites: [
      { from: /route-1-regex/, to: 'route-1-example.html' }
  ]
}


-1

Questo ha funzionato per me: aggiungi semplicemente prima i middleware webpack e il file app.get('*'... successivamente il risolutore index.html,

so express verificherà prima se la richiesta corrisponde a uno dei percorsi forniti da webpack (come: /dist/bundle.jso /__webpack_hmr_) e, in caso contrario, si sposterà nel index.htmlcon il* risolutore.

vale a 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)
})
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.