在 webpack 中集成了 http-proxy-middleware,通过这个中间件可以将请求转发到其他服务。
我们通过 yarn dev 启动设定的端口是 3000,在 webpack.config.js 中配置过。

简单代理

创建新服务

在根目录下创建一个新服务,端口为 4000。
webpack 自带 express。

  1. /** ./server.js **/
  2. let express = require('express');
  3. let app = express();
  4. app.get('/user', function(req, res){
  5. res.json({name: 'jingyu' });
  6. })
  7. app.listen(4000);

请求服务

在入口文件中发起请求

  1. /** ./src/index.js **/
  2. const xhr = new XMLHttpRequest();
  3. xhr.open('get', '/user', true);
  4. xhr.onload = () => {
  5. console.log(xhr.response);
  6. };
  7. xhr.send();
  8. // 或者用 fetch
  9. //fetch('/api/user').then((r) => r.json()).then((value) => console.log(value));

观察我们的 index 文件,我们是在 webpack 的 devServer 中启动的程序,服务端口是 3000,而我们想要访问的是 4000 端口的 user 路由,显然是获取不到的。
访问结果如下:
image.png

webpack 代理

如果想要获取到 4000 端口服务的路由信息,就必须将访问 3000 端口代理到 4000 端口。webpack 集成了这种功能,如下:

  1. //...
  2. module.exports = {
  3. devServer: {
  4. port: 3000, //打开端口
  5. open: true, //是否打开浏览器
  6. compress: true, //是否压缩
  7. static: './dist',
  8. proxy: {
  9. '/user':'http://localhost:4000'
  10. }
  11. }
  12. }
  13. //...

添加 9-10 行,然后我们再试一下
image.png
发现已经成功的请求到了。

裁剪url

但是当我们有多个接口的话,这种方式肯定不合理,如下:

  1. /** ./webpack.config.js **/
  2. //...
  3. module.exports = {
  4. devServer: {
  5. port: 3000, //打开端口
  6. open: true, //是否打开浏览器
  7. compress: true, //是否压缩
  8. static: './dist',
  9. proxy: {
  10. '/user':'http://localhost:4000',
  11. '/user2':'http://localhost:4000',
  12. '/user3':'http://localhost:4000',
  13. //...
  14. }
  15. }
  16. }
  17. //...

这个时候我们前端通常采用在 url 前加一个 api 前缀再访问,如下:

  1. /** ./src/index.js **/
  2. const xhr = new XMLHttpRequest();
  3. xhr.open('get', '/api/user', true); // 添加一个 /api 前缀
  4. xhr.onload = () => {
  5. console.log(xhr.response);
  6. };
  7. xhr.send();

但是后端不会变还是没有 /api 的,所以在实际请求时需要裁剪掉这个api,实现如下:

  1. /** ./webpack.config.js **/
  2. //...
  3. module.exports = {
  4. devServer: {
  5. port: 3000, //打开端口
  6. open: true, //是否打开浏览器
  7. compress: true, //是否压缩
  8. static: './dist',
  9. proxy: {
  10. '/api':{ //跨域并且重写路径
  11. target:'http://localhost:4000',
  12. pathRewrite: { '^/api': '' }// 重写路径
  13. }
  14. }
  15. }
  16. }
  17. //...

这样子代码清爽了,接口又能正常访问,结果如下,和之前一致:
image.png
image.png
这种添加 api 的方式通常用于开发环境,生产环境不添加api,可以通过环境变量来控制。

代理https

  1. /** ./webpack.config.js **/
  2. //...
  3. module.exports = {
  4. devServer: {
  5. port: 3000, //打开端口
  6. open: true, //是否打开浏览器
  7. compress: true, //是否压缩
  8. static: './dist',
  9. proxy: {
  10. '/api':{ //跨域并且重写路径
  11. target:'https://localhost:4000',
  12. secure:false, //代理的是 https
  13. pathRewrite: { '/api': '' }// 重写路径
  14. }
  15. }
  16. }
  17. }
  18. //...

解决跨域

我们在服务中打印一下 headers。

  1. let express = require('express');
  2. let app = express();
  3. app.get('/user', function(req, res){
  4. console.log(req.headers);// 打印 headers
  5. res.json({name: 'jingyu' });
  6. })
  7. app.listen(4000);
  1. {
  2. 'if-none-match': 'W/"11-iy4ymcWRvFhbIwT9wbxYChwvRL0"',
  3. 'accept-language': 'zh-CN,zh;q=0.9',
  4. 'accept-encoding': 'gzip, deflate, br',
  5. referer: 'http://localhost:3000/',
  6. 'sec-fetch-dest': 'empty',
  7. 'sec-fetch-mode': 'cors',
  8. 'sec-fetch-site': 'same-origin',
  9. accept: '*/*',
  10. 'sec-ch-ua-platform': '"macOS"',
  11. 'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36',
  12. 'sec-ch-ua-mobile': '?0',
  13. 'sec-ch-ua': '" Not A;Brand";v="99", "Chromium";v="102", "Google Chrome";v="102"',
  14. connection: 'close',
  15. host: 'localhost:3000'
  16. }

这时我们发现 host 为 localhost:3000。

然后我们在 proxy 中 添加一个 changeOrigin 参数为 true

/** ./webpack.config.js **/
//...

module.exports = {
  devServer: {
    port: 3000, //打开端口
    open: true, //是否打开浏览器
    compress: true, //是否压缩
    static: './dist',
    proxy: {
      '/api':{ //跨域并且重写路径
        target:'https://localhost:4000',
        //secure:false, //代理的是 https
        changeOrigin: true, //主要是把 host 改成访问的服务器地址
        pathRewrite: { '/api': '' }// 重写路径
      }
    }
  }
}

//...

我们打印下 headers

{
  'if-none-match': 'W/"11-iy4ymcWRvFhbIwT9wbxYChwvRL0"',
  'accept-language': 'zh-CN,zh;q=0.9',
  'accept-encoding': 'gzip, deflate, br',
  referer: 'http://localhost:3000/',
  'sec-fetch-dest': 'empty',
  'sec-fetch-mode': 'cors',
  'sec-fetch-site': 'same-origin',
  accept: '*/*',
  'sec-ch-ua-platform': '"macOS"',
  'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36',
  'sec-ch-ua-mobile': '?0',
  'sec-ch-ua': '" Not A;Brand";v="99", "Chromium";v="102", "Google Chrome";v="102"',
  connection: 'close',
  host: 'localhost:4000'
}

这时我们就发现 host 变成了 localhost:4000。
这样可以避免服务器设置 host 限制的情况。

其它一些常用的参数

webpack proxy