Как разрешить webpack-dev-серверу разрешать точки входа с реактивного маршрутизатора

117

Я создаю приложение, которое использует в разработке webpack-dev-server вместе с response-router.

Похоже, что webpack-dev-server построен на предположении, что у вас будет общедоступная точка входа в одном месте (т.е. «/»), тогда как response-router допускает неограниченное количество точек входа.

Мне нужны преимущества webpack-dev-server, особенно функция горячей перезагрузки, которая отлично подходит для производительности, но я все еще хочу иметь возможность загружать маршруты, установленные в response-router.

Как можно было реализовать это так, чтобы они работали вместе? Не могли бы вы запустить экспресс-сервер перед webpack-dev-server таким образом, чтобы это было возможно?

Натан Винерт
источник
У меня есть очень хакерская версия чего-то здесь, но она хрупкая и позволяет сопоставить только простые маршруты: github.com/natew/react-base (см. Make-webpack-config) и (app / routes.js)
Натан Винерт
Удалось ли тебе решить эту проблему, Натан? Если да, то как? Пожалуйста, попробуйте ответить на мой вопрос здесь stackoverflow.com/questions/31091702/… . Спасибо..!
SudoPlz

Ответы:

69

Я настроил прокси для этого:

У вас есть обычный экспресс-веб-сервер, который обслуживает index.html на любом маршруте, за исключением маршрута актива. если это актив, запрос передается на веб-сервер разработчика.

ваши реактивные горячие точки входа по-прежнему будут указывать непосредственно на сервер разработчика webpack, поэтому горячая перезагрузка все еще работает.

Предположим, вы запускаете webpack-dev-server на 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);

теперь сделайте свои точки входа в конфигурации webpack следующим образом:

 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/",
     // ...
 }
Retozi
источник
1
Эй, это круто. На самом деле я подошел к этой настройке незадолго до этого и собирался опубликовать ответ, но я думаю, что вы справились лучше.
Натан Винерт
1
Один вопрос, вроде как не связанный, поэтому я могу при необходимости открыть новый вопрос, но я заметил, что теперь вывод консоли с сервера webpack dev не транслируется. Раньше вы могли смотреть, как он компилируется и видеть рост процентов, теперь он просто блокирует вывод после компиляции.
Натан Винерт
Хорошо сделано. Именно так это и должно быть сделано. Я добавил примечание о output.publicPathопции, которая тоже должна быть абсолютным URL.
Тобиас К.
5
Было бы проще использовать вместо него встроенный прокси-сервер webpack . Таким образом, вы не вмешиваетесь в сам сервер, вы оставляете сервер чистым . Вместо этого вы просто делаете небольшое (3-5 строк) дополнение к конфигурации webpack. Благодаря этому вы изменяете только сценарии разработки для целей разработки и оставляете производственный код (server.js) в покое (в отличие от вашей версии), и я думаю, что это правильный путь.
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
Это должен быть принятый ответ. Из документации по серверу webpack dev: «Если вы используете API истории HTML5, вам, вероятно, потребуется использовать свой index.html вместо ответов 404, что можно сделать, установив historyApiFallback: true» Если я правильно понимаю вопрос, это решит эта проблема.
Себастьян
так просто ... Спасибо!
smnbbrv
1
@smnbbrv Проблем нет. Он фактически использует connect-history-api-fallback внизу, и вы можете передать объект с конкретными параметрами промежуточного программного обеспечения, если хотите, а не просто true.
Juho Vepsäläinen
1
ИЛИ, если вы используете cli,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';
        }
      }
    }
  }
} 
Вернер Вебер
источник
Хорошо поработал для меня
Нат
Сработало красиво! .. Спасибо!
Друмил Бханкхар,
Это просто идеальный ответ, быстрый и легкий.
domino
12

Если вы используете webpack-dev-server с помощью 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 будет откатиться на: 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 . Вот и все. Сервер перезапустится после того, как вы измените любой используемый им компонент.

Однако возникает другая проблема - если вы запустите сервер веб-пакетов из того же процесса, что и экспресс-сервер (как в принятом ответе выше), сервер веб-пакетов также будет перезапущен и каждый раз будет перекомпилировать ваш пакет. Чтобы избежать этого, вы должны запускать свой основной сервер и сервер веб-пакетов в разных процессах, чтобы конвейер перезапускал только ваш экспресс-сервер и не касался веб-пакета. Вы можете сделать это с помощью concurrentlypackage. Вы можете найти пример этого в react-isomorphic-starterkit . В package.json у него есть:

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

который запускает оба сервера одновременно, но в отдельных процессах.

Viacheslav
источник
Означает ли это, что некоторые файлы просматриваются дважды? Такие как общие изоморфные / универсальные файлы?
Дэвид Синклер,
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)
})
Грэм Нортон
источник