Cara mengizinkan webpack-dev-server untuk mengizinkan titik masuk dari react-router

117

Saya membuat aplikasi yang menggunakan webpack-dev-server dalam pengembangan bersama react-router.

Tampaknya webpack-dev-server dibangun dengan asumsi bahwa Anda akan memiliki titik masuk publik di satu tempat (yaitu "/"), sedangkan react-router memungkinkan jumlah titik masuk yang tidak terbatas.

Saya ingin manfaat dari webpack-dev-server, terutama fitur hot reload yang bagus untuk produktivitas, tetapi saya masih ingin memuat rute yang disetel di react-router.

Bagaimana seseorang bisa menerapkannya sedemikian rupa sehingga mereka bekerja sama? Bisakah Anda menjalankan server ekspres di depan webpack-dev-server sedemikian rupa untuk mengizinkan ini?

Nathan Wienert
sumber
Saya memiliki versi yang sangat hacky dari sesuatu di sini, tetapi itu rapuh dan hanya memungkinkan rute sederhana untuk dicocokkan: github.com/natew/react-base (lihat make-webpack-config) dan (app / routes.js)
Nathan Wienert
Apakah Anda berhasil memecahkan masalah ini Nathan? Jika ya, bagaimana caranya? Silakan coba menjawab pertanyaan saya di sini stackoverflow.com/questions/31091702/… . Terima kasih..!
SudoPlz

Jawaban:

69

Saya menyiapkan proxy untuk mencapai ini:

Anda memiliki server web ekspres reguler yang melayani index.html di rute mana pun, kecuali jika itu adalah rute aset. jika itu adalah aset, permintaan akan di-proxy-kan ke web-dev-server

Titik entri react hot Anda masih akan mengarah langsung ke server dev webpack, jadi hot reload masih berfungsi.

Anggaplah Anda menjalankan webpack-dev-server pada 8081 dan proxy Anda pada 8080. File server.js Anda akan terlihat seperti ini:

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

sekarang buat entrypoint Anda di konfigurasi webpack seperti ini:

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

perhatikan panggilan langsung ke 8081 untuk hotreload

juga pastikan Anda meneruskan url absolut ke output.publicPathopsi:

 output: {
     publicPath: "http://localhost:8081/assets/",
     // ...
 }
Retozi
sumber
1
Hei, ini luar biasa. Saya benar-benar tiba di pengaturan ini tidak lama sebelum ini dan akan mengirim jawaban tetapi saya pikir Anda melakukan pekerjaan yang lebih baik.
Nathan Wienert
1
Satu pertanyaan, agak tidak terkait sehingga saya dapat membuka pertanyaan baru jika perlu, tetapi saya perhatikan bahwa sekarang output konsol dari server dev webpack tidak dialirkan. Sebelumnya, Anda dapat menontonnya mengkompilasi dan melihat persentasenya meningkat, sekarang hanya memblokir keluaran setelah kompilasi.
Nathan Wienert
Bagus. Persis seperti inilah yang harus dilakukan. Saya menambahkan catatan tentang output.publicPathopsi, yang seharusnya menjadi url absolut juga.
Tobias K.
5
Akan lebih mudah hanya menggunakan proxy webpack bawaan sebagai gantinya. Dengan demikian Anda tidak ikut campur di server itu sendiri, Anda membiarkan server murni . Sebagai gantinya, Anda hanya melakukan sedikit tambahan (3-5 baris) pada konfigurasi webpack. Berkat itu Anda hanya memodifikasi skrip dev untuk tujuan dev dan membiarkan kode produksi (server.js) dengan damai (tidak seperti di versi Anda) dan imo itulah cara yang tepat untuk pergi.
jalooc
3
Jawaban ini masih benar meski agak kuno. Cara yang lebih mudah tersedia sekarang, cari historyApiFallback.
Eugene Kulabuhov
102

Anda harus menetapkan historyApiFallbackdari WebpackDevServersebagai benar untuk ini bekerja. Berikut adalah contoh kecil (sesuaikan agar sesuai dengan tujuan Anda):

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);
});
Juho Vepsäläinen
sumber
Anda akan kehilangan bilah status di atas index.html Anda, tetapi ini berfungsi dengan baik :)
swennemen
7
Ini harus menjadi jawaban yang diterima. Dari dokumen server webpack dev: "Jika Anda menggunakan API riwayat HTML5, Anda mungkin perlu menyajikan index.html Anda sebagai pengganti 404 tanggapan, yang dapat dilakukan dengan menyetel historyApiFallback: true" Jika saya memahami pertanyaan dengan benar, ini akan menyelesaikannya masalah.
Sebastian
sangat sederhana ... Terima kasih!
smnbbrv
1
@nnbm Tidak ada masalah. Ini sebenarnya menggunakan connect-history-api-fallback di bawahnya dan Anda dapat mengirimkan objek dengan opsi khusus middleware jika Anda mau, bukan hanya true.
Juho Vepsäläinen
1
ATAU jika Anda menggunakan cli,webpack-dev-server --history-api-fallback
Levi
27

Untuk orang lain yang mungkin masih mencari jawaban ini. Saya mengumpulkan bypass proxy sederhana yang mencapai ini tanpa banyak kerumitan dan konfigurasinya masuk ke webpack.config.js

Saya yakin ada banyak cara yang lebih elegan untuk menguji konten lokal menggunakan regex, tetapi ini sesuai dengan kebutuhan saya.

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';
        }
      }
    }
  }
} 
Werner Weber
sumber
Bekerja dengan baik untuk saya
Nath
Bekerja dengan baik! .. Terima kasih!
Dhrumil Bhankhar
Ini hanya jawaban yang sempurna, cepat dan mudah.
domino
12

Jika Anda menjalankan webpack-dev-server menggunakan CLI, Anda dapat mengkonfigurasinya melalui webpack.config.js dengan meneruskan objek devServer:

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

Ini akan mengalihkan ke index.html setiap kali 404 ditemukan.

CATATAN: Jika Anda menggunakan publicPath, Anda juga harus meneruskannya ke devServer:

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

Anda dapat memverifikasi bahwa semuanya telah diatur dengan benar dengan melihat beberapa baris pertama dari output (bagian dengan "404s akan kembali ke: path ").

masukkan deskripsi gambar di sini

Eugene Kulabuhov
sumber
11

Untuk jawaban yang lebih baru, versi webpack saat ini (4.1.1) Anda bisa mengaturnya di webpack.config.js Anda seperti:

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

Bagian yang penting adalah historyApiFallback: true. Tidak perlu menjalankan server khusus, cukup gunakan cli:

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

Saya ingin menambahkan jawaban untuk kasus ketika Anda menjalankan aplikasi isomorfik (yaitu rendering sisi server komponen React.)

Dalam hal ini Anda mungkin juga ingin memuat ulang server secara otomatis ketika Anda mengubah salah satu komponen React Anda. Anda melakukan ini dengan pipingpaket. Yang harus Anda lakukan adalah menginstalnya dan menambahkan require("piping")({hook: true})di suatu tempat di awal server.js Anda . Itu dia. Server akan restart setelah Anda mengubah komponen apa pun yang digunakan olehnya.

Ini menimbulkan masalah lain - jika Anda menjalankan server webpack dari proses yang sama dengan server ekspres Anda (seperti dalam jawaban yang diterima di atas), server webpack juga akan memulai ulang dan akan mengkompilasi ulang bundel Anda setiap saat. Untuk menghindari hal ini, Anda harus menjalankan server utama dan server webpack dalam proses yang berbeda sehingga pemipaan hanya akan memulai ulang server ekspres Anda dan tidak akan menyentuh webpack. Anda dapat melakukan ini dengan concurrentlypaket. Anda dapat menemukan contohnya di react-isomorphic-starterkit . Di package.json dia memiliki:

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

yang menjalankan kedua server secara bersamaan tetapi dalam proses terpisah.

Viacheslav
sumber
Apakah ini berarti beberapa file sedang diawasi dua kali? Seperti file isomorfik / universal bersama?
David Sinclair
1

historyApiFallback juga bisa menjadi objek, bukan Boolean, yang berisi rute.

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

Ini berhasil untuk saya: cukup tambahkan middlewares webpack terlebih dahulu dan app.get('*'...resolver index.html nanti,

jadi express akan terlebih dahulu memeriksa apakah permintaan tersebut cocok dengan salah satu rute yang disediakan oleh webpack (seperti: /dist/bundle.jsatau /__webpack_hmr_) dan jika tidak, maka itu akan pindah ke index.htmldengan *resolver.

yaitu:

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)
})
Graham Norton
sumber