Cách cho phép webpack-dev-server cho phép các điểm vào từ bộ định tuyến phản ứng


117

Tôi đang tạo một ứng dụng sử dụng webpack-dev-server để phát triển cùng với bộ định tuyến phản ứng.

Có vẻ như webpack-dev-server được xây dựng xung quanh giả định rằng bạn sẽ có một điểm vào công khai tại một nơi (tức là "/"), trong khi bộ định tuyến phản ứng cho phép số lượng điểm nhập không giới hạn.

Tôi muốn những lợi ích của webpack-dev-server, đặc biệt là tính năng tải lại nóng rất tốt cho năng suất, nhưng tôi vẫn muốn có thể tải các tuyến đường được đặt trong bộ định tuyến phản ứng.

Làm thế nào một người có thể thực hiện nó để họ làm việc cùng nhau? Bạn có thể chạy một máy chủ tốc hành trước webpack-dev-server theo cách như vậy để cho phép điều này không?


Tôi có một phiên bản cực kỳ hack của một cái gì đó ở đây, nhưng nó rất dễ hỏng và chỉ cho phép các tuyến đơn giản khớp: github.com/natew/react-base (xem make-webpack-config) và (app / Rout.js)
Nathan Wienert

Bạn đã quản lý để giải quyết vấn đề này chưa? Nếu vậy thì thế nào? Hãy cố gắng trả lời câu hỏi của tôi ở đây stackoverflow.com/questions/31091702/ . Cảm ơn bạn..!
SudoPlz

Câu trả lời:


69

Tôi thiết lập một proxy để đạt được điều này:

Bạn có một máy chủ web cấp tốc thông thường phục vụ index.html trên bất kỳ tuyến nào, ngoại trừ nếu đó là tuyến tài sản. nếu đó là một tài sản, yêu cầu sẽ được ủy quyền cho máy chủ web-dev

các điểm truy cập nóng phản ứng của bạn vẫn sẽ trỏ trực tiếp vào máy chủ phát triển webpack, vì vậy tải lại nóng vẫn hoạt động.

Giả sử bạn chạy webpack-dev-server trên 8081 và proxy của bạn ở 8080. Tệp server.js của bạn sẽ trông như thế này:

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

bây giờ làm cho điểm truy cập của bạn trong cấu hình webpack như vậy:

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

lưu ý cuộc gọi trực tiếp tới 8081 để tải hotrel

cũng đảm bảo bạn chuyển một url tuyệt đối cho output.publicPathtùy chọn:

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

1
Này, điều này thật tuyệt vời. Tôi thực sự đã đến thiết lập này ngay trước đó và sẽ đăng một câu trả lời nhưng tôi nghĩ bạn đã làm một công việc tốt hơn.
Nathan Wienert

1
Một câu hỏi, loại không liên quan để tôi có thể mở một câu hỏi mới nếu cần nhưng tôi nhận thấy rằng bây giờ đầu ra giao diện điều khiển từ máy chủ phát triển webpack không được phát trực tuyến. Trước đây, bạn có thể xem nó biên dịch và thấy phần trăm tăng lên, bây giờ nó chỉ chặn các đầu ra sau khi biên dịch.
Nathan Wienert

Hoàn thành tốt. Đây chính xác là cách nó nên được thực hiện. Tôi đã thêm một lưu ý về output.publicPathtùy chọn, đó cũng là một url tuyệt đối.
Tobias K.

5
Thay vào đó, việc sử dụng proxy webpack tích hợp sẽ dễ dàng hơn . Do đó, bạn không can thiệp vào máy chủ, bạn để máy chủ thuần túy . Thay vào đó, bạn chỉ cần thực hiện thêm một chút (3-5 dòng) vào cấu hình webpack. Nhờ đó, bạn chỉ sửa đổi các tập lệnh dev cho mục đích phát triển và giữ nguyên mã sản xuất (server.js) (không giống như trong phiên bản của bạn) và imo đó là cách phù hợp.
jalooc

3
Câu trả lời này vẫn đúng mặc dù một chút ngày. Nhiều cách đơn giản hơn có sẵn bây giờ, tìm kiếm historyApiFallback.
Eugene Kulabuhov

102

Bạn nên thiết lập historyApiFallbackcác WebpackDevServergiá trị đối với này để làm việc. Đây là một ví dụ nhỏ (điều chỉnh để phù hợp với mục đích của bạn):

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

Bạn sẽ bỏ lỡ thanh trạng thái trên đầu index.html, nhưng điều này hoạt động rất tốt :)
swennemen

7
Đây phải là câu trả lời được chấp nhận. Từ tài liệu máy chủ phát triển webpack: "Nếu bạn đang sử dụng API lịch sử HTML5, có lẽ bạn cần phục vụ index.html thay cho câu trả lời 404, có thể được thực hiện bằng cách đặt historyApiFallback: true" Nếu tôi hiểu chính xác câu hỏi này sẽ giải quyết vấn đề.
Sebastian

thật đơn giản ... Cảm ơn bạn!
smnbbrv

1
@smnbbrv Không có probs. Nó thực sự sử dụng kết nối lịch sử-api-dự phòng bên dưới và bạn có thể vượt qua một đối tượng với các tùy chọn cụ thể của phần mềm trung gian nếu bạn muốn thay vì chỉ true.
Juho Vepsäläinen

1
HOẶC nếu bạn đang sử dụng cli,webpack-dev-server --history-api-fallback
Levi

27

Đối với bất cứ ai khác có thể vẫn đang tìm kiếm câu trả lời này. Tôi kết hợp một bỏ qua proxy đơn giản để đạt được điều này mà không gặp nhiều rắc rối và cấu hình đi vào webpack.config.js

Tôi chắc chắn có nhiều cách thanh lịch hơn để kiểm tra nội dung cục bộ bằng regex, nhưng cách này phù hợp với nhu cầu của tôi.

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

Làm việc tốt cho tôi
Nath

Làm việc tốt! Cảm ơn!
Dhrumil Bhankhar

Đây chỉ là câu trả lời hoàn hảo, nhanh chóng và dễ dàng.
domino

12

Nếu bạn đang chạy webpack-dev-server bằng CLI, bạn có thể định cấu hình nó thông qua webpack.config.js truyền đối tượng devServer:

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

Điều này sẽ chuyển hướng đến index.html mỗi khi gặp 404.

LƯU Ý: Nếu bạn đang sử dụng publicPath, bạn cũng sẽ cần chuyển nó cho devServer:

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

Bạn có thể xác minh rằng mọi thứ được thiết lập chính xác bằng cách xem xét một vài dòng đầu ra (phần có "404s sẽ chuyển sang: path ").

nhập mô tả hình ảnh ở đây


11

Để có câu trả lời gần đây hơn, phiên bản hiện tại của webpack (4.1.1), bạn chỉ có thể đặt cái này trong webpack.config.js như sau:

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

Phần quan trọng là historyApiFallback: true. Không cần chạy máy chủ tùy chỉnh, chỉ cần sử dụng cli:

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

2

Tôi muốn thêm vào câu trả lời cho trường hợp khi bạn chạy một ứng dụng đẳng cấu (tức là hiển thị phía máy chủ thành phần React.)

Trong trường hợp này, có lẽ bạn cũng muốn tự động tải lại máy chủ khi bạn thay đổi một trong các thành phần React của mình. Bạn làm điều này với pipinggói. Tất cả bạn phải làm là cài đặt nó và thêm require("piping")({hook: true})một nơi nào đó vào đầu server.js của bạn . Đó là nó. Máy chủ sẽ khởi động lại sau khi bạn thay đổi bất kỳ thành phần nào được sử dụng bởi nó.

Tuy nhiên, điều này lại nảy sinh một vấn đề khác - nếu bạn chạy máy chủ webpack từ cùng một quy trình với máy chủ cấp tốc của bạn (như trong câu trả lời được chấp nhận ở trên), máy chủ webpack cũng sẽ khởi động lại và sẽ biên dịch lại gói của bạn mỗi lần. Để tránh điều này, bạn nên chạy máy chủ chính và máy chủ webpack theo các quy trình khác nhau để đường ống chỉ khởi động lại máy chủ tốc hành của bạn và sẽ không chạm vào webpack. Bạn có thể làm điều này với concurrentlygói. Bạn có thể tìm thấy một ví dụ về điều này trong bộ phản ứng-isomorphic-starterkit . Trong gói.json , anh ta có:

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

mà chạy cả hai máy chủ cùng một lúc nhưng trong các quy trình riêng biệt.


Điều này có nghĩa là một số tập tin đang được xem hai lần? Chẳng hạn như các tập tin đẳng cấu / phổ quát được chia sẻ?
David Sinclair

1

historyApiFallback cũng có thể là một đối tượng thay vì Boolean, chứa các tuyến.

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


-1

Điều này làm việc cho tôi: chỉ cần thêm phần mềm trung gian webpack trước và app.get('*'...bộ giải quyết index.html sau,

do đó thể hiện đầu tiên sẽ kiểm tra xem các yêu cầu phù hợp với một trong những tuyến đường cung cấp bởi webpack (như: /dist/bundle.jshoặc /__webpack_hmr_) và nếu không, sau đó nó sẽ di chuyển đến index.htmlvới *giải quyết.

I E:

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)
})
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.