Як дозволити веб-пакету-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.