Jak zezwolić serwerowi webpack-dev-server, aby zezwolił na punkty wejścia z routera reaktywnego


117

Tworzę aplikację, która korzysta z webpack-dev-server w fazie rozwoju wraz z responsywnym routerem.

Wygląda na to, że webpack-dev-server jest zbudowany w oparciu o założenie, że będziesz mieć publiczny punkt wejścia w jednym miejscu (np. "/"), Podczas gdy react-router pozwala na nieograniczoną liczbę punktów wejścia.

Chcę korzystać z zalet serwera webpack-dev-server, zwłaszcza funkcji ponownego ładowania na gorąco, która jest świetna dla produktywności, ale nadal chcę mieć możliwość ładowania tras ustawionych w routerze reaktywnym.

Jak można to zaimplementować, żeby współpracowały? Czy mógłbyś uruchomić serwer ekspresowy przed serwerem webpack-dev-server w taki sposób, aby to umożliwić?


Mam tutaj bardzo zepsutą wersję czegoś, ale jest ona delikatna i pozwala tylko na dopasowanie prostych tras: github.com/natew/react-base (zobacz make-webpack-config) i (app / tours.js)
Nathan Wienert

Czy udało ci się rozwiązać ten problem, Nathan? Jeśli tak to jak? Spróbuj odpowiedzieć na moje pytanie tutaj stackoverflow.com/questions/31091702/… . Dziękuję Ci..!
SudoPlz

Odpowiedzi:


69

Skonfigurowałem proxy, aby to osiągnąć:

Masz regularny serwer ekspresowy, który obsługuje plik index.html na dowolnej trasie, chyba że jest to trasa zasobów. jeśli jest to zasób, żądanie jest przesyłane przez serwer proxy do serwera WWW

Twoje punkty wejścia na gorąco będą nadal wskazywały bezpośrednio na serwer deweloperski pakietu webpack, więc ponowne ładowanie na gorąco nadal działa.

Załóżmy, że uruchamiasz webpack-dev-server na 8081, a twój serwer proxy na 8080. Twój plik server.js będzie wyglądał następująco:

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

teraz utwórz swoje punkty wejścia w konfiguracji webpacka w następujący sposób:

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

Zwróć uwagę na bezpośrednie połączenie z 8081 w celu ponownego załadowania

upewnij się również, że przekazujesz bezwzględny adres URL do output.publicPathopcji:

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

1
Hej, to jest niesamowite. Właściwie dotarłem do tej konfiguracji na krótko przed tym i miałem zamieścić odpowiedź, ale myślę, że wykonałeś lepszą robotę.
Nathan Wienert

1
Jedno pytanie, trochę niepowiązane, więc mogę otworzyć nowe pytanie, jeśli zajdzie taka potrzeba, ale zauważam, że teraz wyjście konsoli z serwera deweloperskiego pakietu webpack nie jest przesyłane strumieniowo. Wcześniej można było oglądać kompilację i zobaczyć wzrost procentowy, a teraz po kompilacji blokuje tylko wyjścia.
Nathan Wienert

Dobra robota. Dokładnie tak należy to zrobić. Dodałem uwagę o output.publicPathopcji, która również powinna być bezwzględnym adresem URL.
Tobias K.

5
Byłoby łatwiej po prostu użyć wbudowanego serwera proxy w pakiecie internetowym . W ten sposób nie ingerujesz w sam serwer, pozostawiasz serwer czysty . Zamiast tego, po prostu dodasz trochę (3-5 linii) do konfiguracji webpacka. Dzięki temu modyfikujesz tylko skrypty deweloperskie na potrzeby deweloperskie i zostawiasz kod produkcyjny (server.js) w spokoju (inaczej niż w Twojej wersji) i imo to właściwa droga.
jalooc

3
Ta odpowiedź jest nadal poprawna, choć nieco przestarzała. Poszukaj teraz prostszych sposobów historyApiFallback.
Eugene Kulabuhov

102

Należy ustawić historyApiFallbackna WebpackDevServerjak prawdziwe to zadziałało. Oto mały przykład (dostosuj go do swoich celów):

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


7
To powinna być akceptowana odpowiedź. Z dokumentacji serwera deweloperskiego pakietu webpack: „ Jeśli korzystasz z interfejsu API historii HTML5, prawdopodobnie będziesz musiał podawać swój plik index.html zamiast 404 odpowiedzi, co można zrobić, ustawiając historyApiFallback: true” Jeśli dobrze zrozumiem pytanie, to rozwiąże problem.
Sebastian,

takie proste ... Dziękuję!
smnbbrv

1
@smnbbrv No probs. W rzeczywistości używa connect-history-api-fallback pod spodem i możesz przekazać obiekt z opcjami specyficznymi dla oprogramowania pośredniego, jeśli chcesz, zamiast tylko true.
Juho Vepsäläinen

1
LUB jeśli korzystasz z cli,webpack-dev-server --history-api-fallback
Levi

27

Dla każdego, kto może nadal szukać tej odpowiedzi. Złożyłem proste obejście proxy, które osiąga to bez większych problemów, a konfiguracja trafia do webpack.config.js

Jestem pewien, że istnieją znacznie bardziej eleganckie sposoby testowania zawartości lokalnej za pomocą wyrażenia regularnego, ale to działa dla moich potrzeb.

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

Działało dobrze dla mnie
Nath

Ładnie działało! .. Dzięki!
Dhrumil Bhankhar

To po prostu doskonała odpowiedź, szybka i łatwa.
domino

12

Jeśli używasz webpack-dev-server przy użyciu CLI, możesz go skonfigurować poprzez webpack.config.js przekazując obiekt devServer:

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

Spowoduje to przekierowanie do index.html za każdym razem, gdy zostanie napotkany 404.

UWAGA: Jeśli używasz publicPath, musisz również przekazać go do devServer:

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

Możesz sprawdzić, czy wszystko jest poprawnie skonfigurowane, patrząc na kilka pierwszych wierszy danych wyjściowych (część z „404s zostanie zastąpiona: path ”).

wprowadź opis obrazu tutaj


11

Aby uzyskać bardziej aktualną odpowiedź, aktualną wersję pakietu webpack (4.1.1) możesz po prostu ustawić w swoim pliku webpack.config.js, na przykład:

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

Ważna część to historyApiFallback: true. Nie ma potrzeby uruchamiania niestandardowego serwera, po prostu użyj cli:

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

2

Chciałbym dodać do odpowiedzi dla przypadku, gdy uruchamiasz aplikację izomorficzną (tj. Renderujesz komponent React po stronie serwera).

W takim przypadku prawdopodobnie chcesz również automatycznie przeładować serwer, gdy zmienisz jeden z komponentów React. Robisz to z pipingpaczką. Wszystko, co musisz zrobić, to zainstalować go i dodać require("piping")({hook: true})gdzieś na początku server.js . Otóż ​​to. Serwer uruchomi się ponownie po zmianie dowolnego używanego przez niego składnika.

Rodzi to jednak inny problem - jeśli uruchomisz serwer webpacka z tego samego procesu co serwer ekspresowy (jak w zaakceptowanej odpowiedzi powyżej), serwer webpacka również uruchomi się ponownie i za każdym razem przekompiluje twój pakiet. Aby tego uniknąć, powinieneś uruchomić swój główny serwer i serwer webpacka w różnych procesach, tak aby potok zrestartował tylko twój serwer ekspresowy i nie dotykał webpacka. Możesz to zrobić za pomocą concurrentlypakietu. Przykład tego można znaleźć w zestawie startowym do izomorfii . W pliku package.json ma:

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

który uruchamia oba serwery jednocześnie, ale w oddzielnych procesach.


Czy to oznacza, że ​​niektóre pliki są oglądane dwukrotnie? Na przykład udostępnione pliki izomorficzne / uniwersalne?
David Sinclair

1

historyApiFallback może być również obiektem zamiast wartości logicznej, zawierającym trasy.

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


-1

U mnie to zadziałało: po prostu dodaj najpierw oprogramowanie pośredniczące pakietu webpack, a app.get('*'...później program rozpoznawania nazw index.html,

więc express najpierw sprawdzi, czy żądanie pasuje do jednej z tras dostarczonych przez webpack (na przykład: /dist/bundle.jslub /__webpack_hmr_), a jeśli nie, to przejdzie doindex.html z* rezolwerem.

to znaczy:

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)
})
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.