So lassen Sie den Webpack-Dev-Server Einstiegspunkte vom React-Router zu

117

Ich erstelle eine App, die neben dem React-Router auch den Webpack-Dev-Server in der Entwicklung verwendet.

Es scheint, dass der Webpack-Dev-Server auf der Annahme basiert, dass Sie einen öffentlichen Einstiegspunkt an einem Ort haben (dh "/"), während der React-Router eine unbegrenzte Anzahl von Einstiegspunkten zulässt.

Ich möchte die Vorteile des Webpack-Dev-Servers nutzen, insbesondere die Hot-Reloading-Funktion, die sich positiv auf die Produktivität auswirkt, aber ich möchte weiterhin Routen laden können, die im React-Router festgelegt wurden.

Wie könnte man es so umsetzen, dass sie zusammenarbeiten? Könnten Sie einen Express-Server vor dem Webpack-Dev-Server so betreiben, dass dies möglich ist?

Nathan Wienert
quelle
Ich habe hier eine extrem hackige Version von etwas, aber sie ist fragil und lässt nur einfache Routen zu: github.com/natew/react-base (siehe make-webpack-config) und (app / route.js)
Nathan Wienert
Hast du es geschafft, dieses Problem zu lösen, Nathan? Wenn das so ist, wie? Bitte versuchen Sie meine Frage hier zu beantworten stackoverflow.com/questions/31091702/… . Danke dir..!
SudoPlz

Antworten:

69

Ich habe einen Proxy eingerichtet, um dies zu erreichen:

Sie haben einen regulären Express-Webserver, der die index.html auf jeder Route bereitstellt, außer wenn es sich um eine Asset-Route handelt. Wenn es sich um ein Asset handelt, wird die Anforderung an den Web-Dev-Server weitergeleitet

Ihre reaktionsschnellen Einstiegspunkte zeigen weiterhin direkt auf den Webpack-Entwicklungsserver, sodass das Hot-Reloading weiterhin funktioniert.

Angenommen, Sie führen webpack-dev-server unter 8081 und Ihren Proxy unter 8080 aus. Ihre Datei server.js sieht folgendermaßen aus:

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

Machen Sie jetzt Ihre Einstiegspunkte in der Webpack-Konfiguration wie folgt:

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

Beachten Sie den direkten Anruf bei 8081 für Hotreload

Stellen Sie außerdem sicher, dass Sie der output.publicPathOption eine absolute URL übergeben :

 output: {
     publicPath: "http://localhost:8081/assets/",
     // ...
 }
Retozi
quelle
1
Hey, das ist großartig. Ich bin tatsächlich kurz zuvor zu diesem Setup gekommen und wollte eine Antwort posten, aber ich denke, Sie haben einen besseren Job gemacht.
Nathan Wienert
1
Eine Frage, die irgendwie nichts damit zu tun hat, damit ich bei Bedarf eine neue Frage öffnen kann, aber ich stelle fest, dass die Konsolenausgabe vom Webpack-Entwicklungsserver jetzt nicht mehr gestreamt wird. Früher konnte man sehen, wie es kompiliert wurde und wie die Prozentsätze anstiegen. Jetzt blockiert es nur noch die Ausgaben nach dem Kompilieren.
Nathan Wienert
Gut gemacht. Genau so sollte es gemacht werden. Ich habe einen Hinweis zu der output.publicPathOption hinzugefügt , der auch eine absolute URL sein sollte.
Tobias K.
5
Es wäre einfacher, stattdessen nur einen integrierten Webpack-Proxy zu verwenden. Damit Sie nicht in den Server selbst eingreifen, lassen Sie den Server rein . Stattdessen fügen Sie der Webpack-Konfiguration nur eine kleine (3-5 Zeilen) Ergänzung hinzu. Dank dessen ändern Sie nur Entwicklungsskripte für Entwicklungszwecke und lassen den Produktionscode (server.js) in Ruhe (anders als in Ihrer Version), und imo ist dies der richtige Weg.
Jalooc
3
Diese Antwort ist immer noch richtig, wenn auch etwas veraltet. Suchen Sie jetzt nach einfacheren Möglichkeiten historyApiFallback.
Eugene Kulabuhov
102

Sie sollten eingestellt historyApiFallbackvon WebpackDevServerfür diese Arbeit als wahr. Hier ist ein kleines Beispiel (Anpassung an Ihre Zwecke):

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);
});
Juho Vepsäläinen
quelle
Sie werden die Statusleiste oben auf Ihrer index.html vermissen, aber das funktioniert großartig :)
swennemen
7
Dies sollte die akzeptierte Antwort sein. Aus den Dokumenten des Webpack-Dev-Servers: "Wenn Sie die HTML5-Verlaufs-API verwenden, müssen Sie wahrscheinlich Ihre index.html anstelle von 404 Antworten bereitstellen. Dies kann durch Festlegen von historyApiFallback: true erfolgen." Wenn ich die Frage richtig verstehe, wird dies gelöst das Problem.
Sebastian
so einfach ... Danke!
smnbbrv
1
@smnbbrv Keine Probleme. Es verwendet tatsächlich Connect-History-API-Fallback darunter und Sie können ein Objekt mit den Middleware-spezifischen Optionen übergeben, wenn Sie möchten, anstatt nur true.
Juho Vepsäläinen
1
ODER wenn Sie die Cli verwenden,webpack-dev-server --history-api-fallback
Levi
27

Für alle anderen, die noch nach dieser Antwort suchen. Ich habe einen einfachen Proxy-Bypass zusammengestellt, der dies ohne großen Aufwand erreicht, und die Konfiguration geht in die Datei webpack.config.js

Ich bin mir sicher, dass es viel elegantere Möglichkeiten gibt, mit Regex auf lokale Inhalte zu testen, aber dies funktioniert für meine Anforderungen.

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';
        }
      }
    }
  }
} 
Werner Weber
quelle
Hat gut für mich
funktioniert
Hat gut funktioniert! .. Danke!
Dhrumil Bhankhar
Dies ist einfach die perfekte Antwort, schnell und einfach.
Domino
12

Wenn Sie webpack-dev-server mit CLI ausführen, können Sie es über webpack.config.js konfigurieren, indem Sie das devServer-Objekt übergeben:

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

Dies wird jedes Mal, wenn 404 angetroffen wird, zu index.html umgeleitet.

HINWEIS: Wenn Sie publicPath verwenden, müssen Sie es auch an devServer übergeben:

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

Sie können überprüfen, ob alles korrekt eingerichtet ist, indem Sie sich die ersten Zeilen der Ausgabe ansehen (der Teil mit "404s wird auf: Pfad zurückgreifen ").

Geben Sie hier die Bildbeschreibung ein

Eugene Kulabuhov
quelle
11

Für eine neuere Antwort, die aktuelle Version von Webpack (4.1.1), können Sie dies einfach in Ihrer webpack.config.js wie folgt einstellen:

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

Der wichtige Teil ist historyApiFallback: true. Sie müssen keinen benutzerdefinierten Server ausführen. Verwenden Sie einfach die CLI:

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

Ich möchte die Antwort für den Fall ergänzen, dass Sie eine isomorphe App ausführen (dh die React-Komponente serverseitig rendern).

In diesem Fall möchten Sie den Server wahrscheinlich auch automatisch neu laden, wenn Sie eine Ihrer React-Komponenten ändern. Sie tun dies mit dem pipingPaket. Alles was Sie tun müssen, ist es zu installieren und require("piping")({hook: true})irgendwo am Anfang Ihrer server.js hinzuzufügen . Das ist es. Der Server wird neu gestartet, nachdem Sie eine von ihm verwendete Komponente geändert haben.

Dies wirft jedoch ein weiteres Problem auf: Wenn Sie den Webpack-Server über denselben Prozess wie Ihren Express-Server ausführen (wie in der oben akzeptierten Antwort angegeben), wird der Webpack-Server ebenfalls neu gestartet und Ihr Bundle jedes Mal neu kompiliert. Um dies zu vermeiden, sollten Sie Ihren Hauptserver und Ihren Webpack-Server in unterschiedlichen Prozessen ausführen, damit das Piping nur Ihren Express-Server neu startet und das Webpack nicht berührt. Sie können dies mit concurrentlyPaket tun . Ein Beispiel hierfür finden Sie im React-Isomorphic-Starterkit . In der package.json hat er:

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

Dadurch werden beide Server gleichzeitig, jedoch in separaten Prozessen ausgeführt.

Viacheslav
quelle
Bedeutet dies, dass einige Dateien zweimal angesehen werden? Wie die gemeinsam genutzten isomorphen / universellen Dateien?
David Sinclair
1

historyApiFallback kann auch ein Objekt anstelle eines Booleschen Werts sein, das die Routen enthält.

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

Das hat bei mir funktioniert: Fügen Sie einfach zuerst die Webpack-Middleware und später den app.get('*'...Resolver index.html hinzu.

Express prüft daher zunächst, ob die Anfrage mit einer der vom Webpack bereitgestellten Routen übereinstimmt (wie: /dist/bundle.jsoder /__webpack_hmr_). Wenn nicht, wird sie index.htmlmit dem *Resolver an die weitergeleitet.

dh:

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