# 07_脚手架配置代理

📢 大家好,我是Milo

📢 这篇文章是学习 React 中脚手架配置代理的学习笔记

# 引言

# React 使用 axios 发送客户端请求

React 本身只关注于页面,并不包含发送 Ajax 请求的代码,所以一般都是集成第三方的包,或者自己封装的

自己封装的话,比较麻烦,而且也可能考虑不全

常用的有两个库,一个是JQuery,一个是 axios

  1. JQuery 这个比较重,因为 Ajax 服务也只是它这个库里的一小块功能,它主要做的还是 DOM 操作,而这不利于 React ,不推荐使用
  2. axios 这个就比较轻,而且采用 Promise 风格,代码的逻辑会相对清晰,推荐使用

因此我们这里采用 axios 来发送客户端请求,记得在 App.jsx 里引入

import axios from 'axios'
1

# 用代理解决跨域问题

以前,我们在发送请求的时候,经常会遇到一个很重要的问题:跨域!

image-20210827091119837

在我以前的学习中,基本上都需要操作后端服务器代码才能解决跨域的问题,配置请求头,利用 script,这些都需要后端服务器的配合,因此我们前端需要自己解决这个问题的话,就需要这个技术了:代理

在说代理之前,先谈谈为什么会出现跨域?

这个应该是源于浏览器的同源策略。所谓同源(即指在同一个域)就是两个页面具有相同的协议,主机和端口号, 当一个请求 URL 的协议、域名、端口三者之间任意一个与当前页面 URL 不同即为跨域 。

也就是说 xxx:3000xxx:4000 会有跨域问题,xxx:3000abc:3000 有跨域问题

那接下来我们采用配置代理的方式去解决这个问题

关于跨域的问题解决方案,在之后的文章会有总结 ~

# 1. 全局代理

第一种方法,我把它叫做全局代理,因为它直接将代理配置在了配置文件 package.json

"proxy":"http://localhost:5000"  
// "proxy":"请求的地址"
1
2

这样配置代理时,首先会在抓原请求地址上访问,如果访问不到文件,就会转发到这里配置的地址上去请求

转发

我们需要做的就是在我们的请求代码中,将请求的地址改到转发的地址,即可

# 1.1 启动服务器

用天禹老师给我们的测试代理服务器来验证,这个文件在 https://github.com/Milo980412/NOTE_REACT/tree/master/React基础/06_react B站视频资料/06_其他/测试代理服务器 (opens new window)

用vscode打开这文件,里面有 server1.jsserver2.js ,主要用这俩文件来验证

server1.js 里有学生数据

server2.js 里有汽车数据

测试代理服务器
├─ node_modules
├─ server1.js
├─ server2.js
├─ package.json
└─ yarn.lock
1
2
3
4
5
6

建议在vscode里启动终端,然后启动 sever1.js 服务器

node server1.js
1

此时服务器1启动成功了,请求学生信息地址为:http://localhost:5000/students

# 1.2 配置代理

设置全局代理,在配置文件 package.json

"proxy":"http://localhost:5000"  
// "proxy":"请求的地址"
1
2

在我们的 React 脚手架 App.jsx 里首先引入 axios

import axios from 'axios'
1

创建 button 按钮并绑定事件 getStudentData

<button onClick={this.getStudentData}>点我获取学生数据</button>
1

编写 getStudentData 函数, get 学生数据

getStudentData = () => {
    axios.get('http://localhost:3000/students').then(
        response => { console.log('成功了', response.data) },
        error => { console.log('失败了', error) }
    )
}
1
2
3
4
5
6

⚠️注意:这样配置代理时,首先会在抓原请求地址(3000端口)上访问,如果访问不到文件,就会转发到我们配置的地址(5000端口)上去请求

启动 React 脚手架

npm start
1

按下按钮,我们就得到了 server1.js 的学生数据

jMZtGq.png

# 1.3 优缺点

优点:配置简单,前端请求资源可以不加任何前缀

缺点:不能配置多个代理

工作方式:上述方法配置代理,当求情了 3000 不存在的资源,那么该请求会转发给 5000(优先匹配前端资源)

所以这种方法我们使用时会有一些问题:

  1. 它会先向我们请求的地址,也就是这里的 3000 端口下请求数据,如果在 3000 端口中存在我们需要访问的文件,会直接返回,不会再去转发
  2. 同时因为这种方式采用的是全局配置的关系,导致只能转发到一个地址,不能配置多个代理

# 2. 单独配置

这是自己起的名字,这种配置方式,可以给多个请求配置代理,非常不错

它的工作原理和全局配置是一样的,但是写法不同

# 2.1 启动服务器

server1.js 里有学生数据

server2.js 里有汽车数据

建议在vscode里打开两个终端,然后分别启动 sever1.jssever2.js 服务器

node server1.js
1

此时服务器1启动成功了,请求学生信息地址为:http://localhost:5000/students

node server2.js
1

服务器2启动成功了,请求汽车信息地址为:http://localhost:5001/cars

# 2.2 配置代理

首先 我们需要在Reac t脚手架 src 目录下,创建代理配置文件 setupProxy.js

⚠️注意:这个文件只能叫这个名字,脚手架在启动的时候,会自动执行这些文件

然后setupProxy.js 里配置代理

配置一个代理的完整代码如下:

⚠️注意:这里与天禹老师的代码有出入,现在是2022年6月8日,天禹老师的代码会报错,无法显示页面

const {createProxyMiddleware} = require('http-proxy-middleware')
module.exports = function(app) {
  app.use(
    createProxyMiddleware('/api1',{ //遇见/apil前缀的请求就会触发
      target: 'http://localhost:5000', //配置转发目标地址
      changeOrigin: true, //控制服务器接收到的请求头中host字段的值
      pathRewrite: {'^/api1': ''} //去除请求前缀址(必须配置)
    }),
    createProxyMiddleware('/api2', { 
      target: 'http://localhost:5001', 
      changeOrigin: true, 
      pathRewrite: {'^/api1': ''}
    })
  )
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

在我们的 React 脚手架 App.jsx 里再创建一个 button 按钮并绑定事件 getCarData

<button onClick={this.getCarData}>点我获取汽车数据</button>
1

重写 getStudentData 函数,并新编写 getCarData 函数

getStudentData = () => {
    axios.get('http://localhost:3000/api1/students').then(
        response => { console.log('成功了', response.data) },
        error => { console.log('失败了', error) }
    )
}
getCarData = () => {
    axios.get('http://localhost:3000/api2/students').then(
        response => { console.log('成功了', response.data) },
        error => { console.log('失败了', error) }
    )
}
1
2
3
4
5
6
7
8
9
10
11
12

路径前缀分别为 api1 和 api2 ,启动 React 脚手架

npm start
1

按下按钮,我们就得到了 server1.js 的学生数据和 server2.js 的汽车数据

jMehpq.png

# 2.3 配置具体的代理规则,我们大致讲讲这些是什么意思

  1. 首先我们需要引入这个 http-proxy-middleware 中间件
const {createProxyMiddleware} = require('http-proxy-middleware')
1
  1. 然后需要导出一个对象,这里建议使用函数 function 并暴露它,使用对象的话兼容性不大好
module.exports = function(app) {}
1
  1. 然后需要在 app.use 中配置代理规则,首先 createProxyMiddleware 接收的第一个参数 '/api1' 是需要转发的请求,我的理解是一个标志的作用,当有这个标志的时候,预示着我们需要采用代理,例如 /api1 ,就需要在我们 axios 的请求路径中,加上 /api1 ,这样所有添加了 /api1 前缀的请求都会转发到这里
module.exports = function(app) {
  app.use(createProxyMiddleware('/api1'))
}
1
2
3
  1. 第二个参数接受的是一个对象,用于配置代理。
createProxyMiddleware('/api1', { 
  target: 'http://localhost:5000', //配置转发目标地址
  changeOrigin: true, //控制服务器接收到的请求头中host字段的值
  pathRewrite: {'^/api1': ''} //去除请求前缀址,重写请求路径(必须配置)
}
1
2
3
4
5

target 属性用于配置转发目标地址,也就是我们数据的地址


changeOrigin 属性用于控制服务器收到的响应头中 host 字段的值

  • 如果不用 changeOrigin 属性,我们在 setupProxy.js 的 app.use 里加一句
console.log('请求来自于',request.get('Host'));
1

就会返回 请求来自于 localhost:3000,也就是说服务器知道你是 3000 端口来的

  • 如果使用 changeOrigin 属性且为 true

就会返回 请求来自于 localhost:5000,收到的 host 就为要请求数据所在的地址,可以理解成伪装效果


pathRewrite 属性用于去除请求前缀(重写请求路径)

因为我们通过代理请求时,需要在请求地址前添加一个标志,但是实际的地址是不存在这个标志的,所以我们一定要去除这个前缀,这里采用的有点类似于正则替换的方式

  • 如果不用 pathRewrite 属性,我们在 setupProxy.js 的 app.use 里加一句
console.log('请求的地址',request.url);
1

首先 React页面拿不到数据,其次服务器端会返回 请求的地址 /api1/students,路径错误!

  • 如果使用 pathRewrite 属性,去除请求前缀址

服务器端会返回 请求的地址 /students, React页面可以拿到数据


# 2.4 优缺点

优点:可以配置多个代理,可以灵活的控制请求是否走代理

缺点:配置繁琐,前端请求资源时必须加前缀


关于脚手架配置代理的内容就到这里啦!

非常感谢您的阅读,欢迎提出你的意见,有什么问题欢迎指出,谢谢!🎈