Як дозволити веб-пакету-dev-серверу дозволити вхідні точки від react-router


117

Я створюю додаток, який використовує webpack-dev-сервер у розробці разом із реактор-роутером.

Схоже, що webpack-dev-сервер побудований на основі припущення, що ви матимете публічну точку входу в одному місці (тобто "/"), тоді як реактор-маршрутизатор передбачає необмежену кількість точок входу.

Я хочу отримати переваги webpack-dev-сервера, особливо функцію гарячого перезавантаження, яка чудово підходить для продуктивності, але я все одно хочу вміти завантажувати маршрути, встановлені в react-router.

Як можна реалізувати це таким чином, щоб вони працювали разом? Чи можете ви запустити експрес-сервер перед webpack-dev-сервером таким чином, щоб дозволити це?


У мене тут надзвичайно прискіплива версія чогось, але вона неміцна і дозволяє лише узгоджувати прості маршрути: github.com/natew/react-base (див. Make-webpack-config) та (app / route.js)
Nathan Wienert

Чи вдалося вам вирішити цю проблему, Натан? Якщо так, як? Будь ласка , спробуйте відповісти на моє запитання тут stackoverflow.com/questions/31091702 / ... . Дякую..!
SudoPlz

Відповіді:


69

Я створив проксі для цього:

У вас є звичайний експрес-веб-сервер, який обслуговує index.html на будь-якому маршруті, за винятком випадків, коли це маршрут активу. якщо це актив, запит проксі передається на веб-сервер dev

ваші гарячі точки входу в реакцію все одно будуть вказувати безпосередньо на сервер розробників webpack, тому гаряче перезавантаження все ще працює.

Припустимо, ви запускаєте webpack-dev-сервер на 8081, а ваш проксі - на 8080. Ваш файл server.js буде виглядати приблизно так:

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

тепер зробіть свої вхідні точки в конфігурації веб-пакету так:

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

відзначте прямий дзвінок на номер 8081 для завантаження

також переконайтеся, що ви передаєте абсолютний URL для output.publicPathпараметра:

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

1
Гей, це приголомшливо. Я насправді прийшов до цього налаштування незадовго до цього і збирався опублікувати відповідь, але, думаю, ви зробили кращу роботу.
Nathan Wienert

1
Одне запитання, якесь не пов’язане між собою, тож я можу відкрити нове запитання, якщо це потрібно, але зауважую, що зараз консольний вихід із сервера розробників webpack не передається потоком. Якщо раніше ви могли спостерігати, як він збирається, і бачити, як відсоток збільшується, тепер він просто блокує результати після компіляції.
Nathan Wienert

Приємно зроблено. Саме так слід робити. Я додав примітку про output.publicPathваріант, який також повинен бути абсолютним URL-адресою.
Тобіас К.

5
Було б простіше просто використовувати вбудований проксі веб -пакет . Таким чином, ви не втручаєтесь у сам сервер, ви залишаєте сервер чистим . Натомість ви просто зробите невелике (3-5 рядків) додаток до конфігурації вебпакету. Завдяки цьому ви змінюєте лише сценарії розробки для цілей розробника та залишаєте у виробництві код (server.js) у спокої (на відміну від вашої версії) та imo - це правильний шлях.
jalooc

3
Ця відповідь як і раніше правильна, хоч трохи датована. Зараз доступні більш прості способи, шукайте historyApiFallback.
Євген Кулабухов

102

Ви повинні встановити historyApiFallbackв WebpackDevServerякості істинно для цього до роботи. Ось невеликий приклад (налаштування відповідно до ваших цілей):

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

Ви пропустите смужку статусу вгорі свого index.html, але це чудово працює :)
swennemen

7
Це має бути прийнятою відповіддю. Документи із сервера веб-упаковки розробників: "Якщо ви використовуєте API історії HTML5, вам, ймовірно, потрібно обслуговувати ваш index.html замість 404 відповідей, що можна зробити, встановивши historyApiFallback: true" Якщо я правильно розумію, це вирішить проблема.
Себастьян

так просто ... Дякую!
smnbbrv

1
@smnbbrv Немає проблем. Він фактично використовує підключення-історія-api-backback під ним, і ви можете передавати об’єкт із певними опціями середнього програмного забезпечення, якщо ви хочете, а не просто true.
Juho Vepsäläinen

1
АБО якщо ви користуєтесь кліпом,webpack-dev-server --history-api-fallback
Леві

27

Для тих, хто ще може шукати цю відповідь. Я зібрав простий пропуск проксі, який досягає цього без особливих клопотів, і конфігурація переходить у webpack.config.js

Я впевнений, що є набагато більш елегантні способи тестування на локальний вміст, використовуючи регулярний вираз, але це працює для моїх потреб.

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

Добре працював для мене
Nath

Працювали чудово! .. Дякую!
Dhrumil Bhankhar

Це просто ідеальна відповідь, швидко та легко.
доміно

12

Якщо ви використовуєте webpack-dev-сервер за допомогою CLI, ви можете налаштувати його через webpack.config.js, передаючи об’єкт devServer:

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

Це буде перенаправляти на index.html кожного разу, коли буде виявлено 404.

ПРИМІТКА. Якщо ви використовуєте publicPath, вам також потрібно буде передати його devServer:

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

Ви можете переконатися, що все налаштовано правильно, переглянувши перші кілька рядків виводу (частина з "404s willbackback to: path ").

введіть тут опис зображення


11

Для більш пізньої відповіді, поточної версії webpack (4.1.1) ви можете просто встановити це у своєму webpack.config.js таким чином:

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

Важлива частина historyApiFallback: true. Не потрібно запускати користувальницький сервер, просто використовуйте cli:

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

2

Я хотів би додати відповідь у випадку, коли ви запускаєте ізоморфний додаток (тобто надання сервера на компоненті React.)

У цьому випадку, ймовірно, ви також хочете автоматично перезавантажити сервер, коли ви зміните один із своїх компонентів React. Ви робите це з pipingпакетом. Все, що вам потрібно зробити, це встановити його і додати require("piping")({hook: true})десь на початку вашого server.js . Це воно. Сервер перезапуститься після зміни будь-якого використовуваного ним компонента.

Це викликає ще одну проблему - якщо ви запускаєте сервер вебпакету з того ж процесу, що і ваш експрес-сервер (як у прийнятій відповіді вище), сервер вебпакету також перезапуститься і буде перекомпілювати ваш пакет кожен раз. Щоб уникнути цього, слід запустити основний сервер і сервер вебпакету в різних процесах, щоб трубопровід перезапустив лише ваш експрес-сервер і не торкнувся вебпакету. Це можна зробити за допомогою concurrentlyпакету. Ви можете знайти приклад цього в реакційно-ізоморфному-стартеркіті . В package.json у нього є:

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

який запускає обидва сервери одночасно, але в окремих процесах.


Це означає, що деякі файли переглядаються двічі? Такі як спільні ізоморфні / універсальні файли?
Девід Сінклер

1

historyApiFallback також може бути об'єктом замість булева, що містить маршрути.

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


-1

Це працювало для мене: просто додайте спочатку проміжні веб-пакети та app.get('*'...пізніше розв’язчик index.html,

таким чином Express спочатку перевірить, чи відповідає запит одному з маршрутів, наданих webpack (наприклад: /dist/bundle.jsабо /__webpack_hmr_), а якщо ні, то він перейде до розділу index.htmlразом із *роздільником.

тобто:

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)
})
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.