私は、react-routerと一緒に開発中のwebpack-dev-serverを使用するアプリを作成しています。
Webpack-dev-serverは、1つの場所(つまり「/」)にパブリックエントリポイントがあるという前提に基づいて構築されているのに対し、react-routerは無制限のエントリポイントを許可しているようです。
Webpack-dev-serverの利点、特に生産性に優れたホットリロード機能が必要ですが、react-routerで設定されたルートをロードできるようにしたいです。
彼らが一緒に動作するようにどのように実装できますか?これを可能にするような方法でwebpack-dev-serverの前でエクスプレスサーバーを実行できますか?
これを実現するためにプロキシを設定しました。
アセットルートである場合を除き、任意のルートでindex.htmlを提供する通常の高速Webサーバーがあります。アセットの場合、リクエストはweb-dev-serverにプロキシされます
反応するホットエントリポイントは引き続きwebpack devサーバーを直接指すため、ホットリロードは引き続き機能します。
8081でwebpack-dev-serverを実行し、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 configでエントリポイントを作成します。
entry: [
'./src/main.js',
'webpack/hot/dev-server',
'webpack-dev-server/client?http://localhost:8081'
]
hotreloadの8081への直接呼び出しに注意してください
また、output.publicPath
オプションに絶対URLを渡すようにしてください:
output: {
publicPath: "http://localhost:8081/assets/",
// ...
}
これが機能するには、historyApiFallback
のWebpackDevServer
をtrueに設定する必要があります。以下に小さな例を示します(目的に合わせて微調整します):
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);
});
まだこの答えを探している人のために。面倒なことなくこれを実現する簡単なプロキシバイパスを作成し、configを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';
}
}
}
}
}
CLIを使用してwebpack-dev-serverを実行している場合、webpack.config.jsを介してdevServerオブジェクトを渡して設定できます。
module.exports = {
entry: "index.js",
output: {
filename: "bundle.js"
},
devServer: {
historyApiFallback: true
}
}
これにより、404が検出されるたびにindex.htmlにリダイレクトされます。
注:publicPathを使用している場合は、devServerにも渡す必要があります。
module.exports = {
entry: "index.js",
output: {
filename: "bundle.js",
publicPath: "admin/dashboard"
},
devServer: {
historyApiFallback: {
index: "admin/dashboard"
}
}
}
出力の最初の数行を見ると、すべてが正しく設定されていることを確認できます(「404s fallback to:path」)。
より最近の答えとして、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"
},
同型アプリを実行する場合(つまり、Reactコンポーネントをサーバー側でレンダリングする場合)の答えに追加したいと思います。
この場合、Reactコンポーネントの1つを変更したときにサーバーを自動的にリロードすることもできます。これはpiping
パッケージで行います。あなたがしなければならないのはそれをインストールし、あなたの最初のどこかにrequire("piping")({hook: true})
を追加するだけですserver.js。それでおしまい。サーバーは、使用するコンポーネントを変更すると再起動します。
ただし、これにより別の問題が発生します-エクスプレスサーバーと同じプロセスからwebpackサーバーを実行すると(上記の回答のように)、webpackサーバーも再起動し、毎回バンドルを再コンパイルします。これを回避するには、メインサーバーとwebpackサーバーを異なるプロセスで実行して、パイプがエクスプレスサーバーのみを再起動し、webpackに触れないようにする必要があります。 concurrently
パッケージでこれを行うことができます。この例は react-isomorphic-starterkit にあります。 package.jsonで彼は:
"scripts": {
...
"watch": "node ./node_modules/concurrently/src/main.js --kill-others 'npm run watch-client' 'npm run start'"
},
両方のサーバーを同時に実行しますが、別々のプロセスで実行します。
historyApiFallback
は、ブール値の代わりに、ルートを含むオブジェクトにすることもできます。
historyApiFallback: navData && {
rewrites: [
{ from: /route-1-regex/, to: 'route-1-example.html' }
]
}
すべての場合に当てはまるわけではありませんが、devServerのpublicPath: '/'
オプションは、深いルートの問題を修正する最も簡単なソリューションのようです。 https://github.com/ReactTraining/react-router/issues/676
これは私にとってはうまくいきました。最初にwebpackミドルウェアを追加し、後で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)
})