axios学习笔记

视频来源:黑马

笔记参考:努力学习的汪

学习时间:2022年2月28日

1 预备工具

1.1 安装

json-server可用于模拟后端。

  • 安装
1
npm install -g json-server
  • axios文件夹下创建db.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{
"post": [
{
"id": 1,
"title": "json-server",
"author": "typicode"
},
{
"id": 2,
"title": "Mark
"author": "typicode"
}
],
"comments": [
{
"id": 1,
"body": "some comment",
"postId": 1
}
],
"profile": {
"name": "typicode"
}
}
  • 在当前文件夹下启动json-server服务
1
json-server --watch db.json

注意事项

在执行上面的命令时,可能会报以下错误:

1
无法加载文件 json-server.ps1,因为在此系统上禁止运行脚本。

解决办法:

  • 以管理员身份运行vscode
  • 在终端执行:
1
2
get-ExecutionPolicy # 显示Restricted,表示状态是禁止的
set-ExecutionPolicy RemoteSigned

1.2 使用

服务启动后,我们可以以Rest的方式请求这些地址:

image-20220228195916450

例如获取id为2的post数据,可访问接口:http://localhost/post/2

image-20220228200029859

2 axios的理解与使用

2.1 概述

2.1.1 axios是什么

  1. 前端最流行的 ajax 请求库
  2. react/vue 官方都推荐使用 axios 发 ajax 请求
  3. 文档: https://github.com/axios/axios

2.1.2 axios特点

  1. 基于 xhrXMLHttpRequest) + promise异步 ajax 请求库
  2. 浏览器端/node 端都可以使用
  3. 支持请求/响应拦截器
  4. 支持请求取消
  5. 请求/响应数据转换
  6. 批量发送多个请求

2.1.3 axios常用语法

  1. axios(config): 通用/最本质的发任意类型请求的方式
  2. axios(url[, config]): 可以只指定 url 发 get 请求
  3. axios.request(config): 等同于 axios(config)
  4. axios.get(url[, config]): 发 get 请求
  5. axios.delete(url[, config]): 发 delete 请求
  6. axios.post(url[, data, config]): 发 post 请求
  7. axios.put(url[, data, config]): 发 put 请求
  8. axios.defaults.xxx: 请求的默认全局配置
  9. axios.interceptors.request.use(): 添加请求拦截器
  10. axios.interceptors.response.use(): 添加响应拦截器
  11. axios.create([config]): 创建一个新的 axios(它没有下面的功能)
  12. axios.Cancel(): 用于创建取消请求的错误对象
  13. axios.CancelToken(): 用于创建取消请求的 token 对象
  14. axios.isCancel(): 是否是一个取消请求的错误
  15. axios.all(promises): 用于批量执行多个异步请求
  16. axios.spread(): 用来指定接收所有成功数据的回调函数的方法

2.1.4 原理图

image-20220228200820716

2.2 基本使用

2.2.1 安装

有多种方式安装axios:

  • 使用npm或yarn全局安装:项目中使用
1
npm install axios
1
yarn add axios
  • 通过在页面中CDN来引入:学习使用
1
<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.26.0/axios.js"></script>
1
<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.26.0/axios.min.js"></script>

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.26.0/axios.js"></script>
</head>
<body>
<script>
console.log(axios);
</script>
</body>
</html>

image-20220228201451099

2.2.2 发送不同类型的请求

对应常用语法的第1条。

  • 基础页面:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.26.0/axios.js"></script>
<link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/4.6.1/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container">
<h2 class="page-header">基本使用</h2>
<button class="btn btn-primary">发送GET请求</button>
<button class="btn btn-warning">发送POST请求</button>
<button class="btn btn-success">发送PUT请求</button>
<button class="btn btn-danger">发送DELETE请求</button>
</div>
<script>
// 获取按钮
const btns = document.querySelectorAll("button");
</script>
</body>
</html>

image-20220228202915447

  • 发送GET请求

    • 代码

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      // 发送GET请求
      btns[0].onclick = function() {
      axios({
      // 请求类型
      method: "GET",
      // 请求URL
      url: "http://localhost:3000/posts/2"
      }).then(response => {
      // 打印响应信息
      console.log(response);
      });
      };
    • 响应信息

      image-20220228203206836

  • 发送POST请求:例如新增一个文档

    • 代码

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      // 发送POST请求
      // 添加一篇新的文章
      btns[1].onclick = function() {
      axios({
      // 请求类型
      method: "POST",
      // 请求URL
      url: "http://localhost:3000/posts",
      // 设置请求体
      data: {
      title: "Today",
      author: "Hongyi"
      }
      }).then(response => {
      console.log(response);
      });
      };
    • 执行结果:

      image-20220228203315038

  • 发送PUT请求:例如更新一篇文档

    • 代码:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      // 发送PUT请求
      btns[2].onclick = function() {
      axios({
      // 请求类型
      method: "PUT",
      // 请求URL
      url: "http://localhost:3000/posts/3",
      // 设置请求体
      data: {
      title: "Today",
      author: "Zhangsan"
      }
      }).then(response => {
      console.log(response);
      });
      };
    • 执行结果:

      image-20220228203427246

      image-20220228203451479

  • 发送DELETE请求

    • 代码:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      // 发送DELETE请求
      btns[3].onclick = function() {
      axios({
      // 请求类型
      method: "DELETE",
      // 请求URL
      url: "http://localhost:3000/posts/3"
      }).then(response => {
      console.log(response);
      });
      };

总结

axios传入一个对象,对象的属性必填的有:

  • method:请求方法
  • url:请求的接口地址

2.2.3 其他方式发送请求

对应常用语法的3-7条。

  • axios.request(config),等同于axios(config)
1
2
3
4
5
6
7
8
9
// 发送GET请求
btns[0].onclick = function() {
axios.request({
method: "GET",
url: "http://localhost:3000/comments"
}).then(response => {
console.log(response);
});
}
  • 发送post请求
1
2
3
4
5
6
7
8
9
10
11
12
btns[1].onclick = function() {
axios.post(
// url
"http://localhost:3000/comments",
// 请求体
{
"body": "Good morning",
"postId": 2
}).then(response => {
console.log(response);
});
};

其余请求写法类似。

2.2.4 请求响应结果的结构

即回调函数中response的结构。以GET请求为例:

image-20220228205918925

2.2.5 请求的配置对象

即发送axios请求时:axios(config)中的config对象

These are the available config options for making requests. Only the url is required. Requests will default to GET if method is not specified.

以下字段只摘取重要的字段进行注释:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
{
url: '/user',

method: 'get', // default

// 设定url的基础结构
baseURL: 'https://some-domain.com/api/',

// 对请求的数据进行处理
transformRequest: [function (data, headers) {
// Do whatever you want to transform the data

return data;
}],

transformResponse: [function (data) {
// Do whatever you want to transform the data

return data;
}],

// `headers` are custom headers to be sent
headers: {'X-Requested-With': 'XMLHttpRequest'},

// `params` are the URL parameters to be sent with the request
// Must be a plain object or a URLSearchParams object
// 例如 url?a=100&ID=12345
params: {
a: 100,
ID: 12345
},

// 对请求参数做序列化
paramsSerializer: function (params) {
return Qs.stringify(params, {arrayFormat: 'brackets'})
},

// 请求体数据,作为请求体数据
// 对象的写法
data: {
firstName: 'Fred'
},

// 请求数据,作为url的参数
// 字符串的写法
data: 'Country=Brasil&City=Belo Horizonte',

// 超时设置,ms
timeout: 1000, // default is `0` (no timeout)

// `withCredentials` indicates whether or not cross-site Access-Control requests
// should be made using credentials
withCredentials: false, // default

adapter: function (config) {
/* ... */
},

auth: {
username: 'janedoe',
password: 's00pers3cret'
},

// `responseType` indicates the type of data that the server will respond with
// options are: 'arraybuffer', 'document', 'json', 'text', 'stream'
// browser only: 'blob'
responseType: 'json', // default

// `responseEncoding` indicates encoding to use for decoding responses (Node.js only)
// Note: Ignored for `responseType` of 'stream' or client-side requests
responseEncoding: 'utf8', // default

// `xsrfCookieName` is the name of the cookie to use as a value for xsrf token
xsrfCookieName: 'XSRF-TOKEN', // default

// `xsrfHeaderName` is the name of the http header that carries the xsrf token value
xsrfHeaderName: 'X-XSRF-TOKEN', // default

// `maxContentLength` defines the max size of the http response content in bytes allowed in node.js
maxContentLength: 2000,

// `maxBodyLength` (Node only option) defines the max size of the http request content in bytes allowed
maxBodyLength: 2000
// ...以下不再列出
}

2.2.6 默认配置

对应常用语法的第8条:axios.defaults.xxx: 请求的默认全局配置

例如配置默认请求方式、url携带参数等。

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 获取按钮
const btns = document.querySelectorAll("button");
// 默认配置
axios.defaults.method = "GET";
axios.defaults.baseURL = "http://localhost:3000";
axios.defaults.params = {
id: 100
};
axios.defaults.timeout = 3000;

// 默认发送GET请求
btns[0].onclick = function() {
axios({
url: "/posts"
}).then(response => {
console.log(response);
});
};

image-20220301142442183

2.2.7 创建实例对象发送请求

对应常用语法第11条:axios.create([config]): 创建一个新的 axios

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 创建axios实例对象
const joke = axios.create({
baseURL: "https://api.apiopen.top",
timeout: 2000
});
// 这里的joke与axios对象的功能 几乎一样
console.log(joke);

// 方式1
joke({
method: "GET",
url: "/getJoke"
}).then(response => {
console.log(response);
})

// 方式2
joke.get("/getJoke").then(response => {
console.log(response.data);
});

2.2.8 拦截器

  • 拦截器(Interceptor)的分类
    • 请求拦截器
    • 响应拦截器
  • 拦截器的作用
    • 在请求发送前和接收响应前,对请求数据和响应数据做一些处理

代码示例

  • 情形①:请求拦截器成功运行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 设置请求拦截器
axios.interceptors.request.use(function (config) {
console.log("请求拦截器 成功");
return config;
}, function (error) {
console.log("请求拦截器 失败")
return Promise.reject(error);
});

// 设置响应拦截器
axios.interceptors.response.use(function (response) {
console.log("响应拦截器 成功");
return response;
}, function (error) {
console.log("响应拦截器 失败");
return Promise.reject(error);
});

// 发送请求
axios({
method: "GET",
url: "http://localhost:3000/posts"
}).then(response => {
console.log("自定义回调处理成功的结果");
}).catch(reason => {
console.log("自定义失败回调");
});
1
2
3
请求拦截器 成功
响应拦截器 成功
自定义回调处理成功的结果
  • 情形②:请求拦截器抛出错误
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// 设置请求拦截器
axios.interceptors.request.use(function (config) {
console.log("请求拦截器 成功");
// return config;
// 抛出一个异常
throw "参数异常";
}, function (error) {
console.log("请求拦截器 失败")
return Promise.reject(error);
});

// 设置响应拦截器
axios.interceptors.response.use(function (response) {
console.log("响应拦截器 成功");
return response;
}, function (error) {
console.log("响应拦截器 失败");
return Promise.reject(error);
});

// 发送请求
axios({
method: "GET",
url: "http://localhost:3000/posts"
}).then(response => {
console.log("自定义回调处理成功的结果");
}).catch(reason => {
console.log("自定义失败回调");
});
1
2
3
请求拦截器 成功
响应拦截器 失败
自定义失败回调
  • 情形③:设置分别两个请求和响应拦截器,注意拦截顺序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// 设置请求拦截器
axios.interceptors.request.use(function (config) {
console.log("请求拦截器 成功 - 1号");
return config;
}, function (error) {
console.log("请求拦截器 失败 - 1号")
return Promise.reject(error);
});

// 设置请求拦截器
axios.interceptors.request.use(function (config) {
console.log("请求拦截器 成功 - 2号");
return config;
}, function (error) {
console.log("请求拦截器 失败 - 2号")
return Promise.reject(error);
});

// 设置响应拦截器
axios.interceptors.response.use(function (response) {
console.log("响应拦截器 成功 - 1号");
return response;
}, function (error) {
console.log("响应拦截器 失败 - 1号");
return Promise.reject(error);
});

// 设置响应拦截器
axios.interceptors.response.use(function (response) {
console.log("响应拦截器 成功 - 2号");
return response;
}, function (error) {
console.log("响应拦截器 失败 - 2号");
return Promise.reject(error);
});

// 发送请求
axios({
method: "GET",
url: "http://localhost:3000/posts"
}).then(response => {
console.log("自定义回调处理成功的结果");
}).catch(reason => {
console.log("自定义失败回调");
});
1
2
3
4
5
请求拦截器 成功 - 2号
请求拦截器 成功 - 1号
响应拦截器 成功 - 1号
响应拦截器 成功 - 2号
自定义回调处理成功的结果

请求拦截器的拦截顺序是倒序的,而响应拦截器的拦截顺序是顺序的。

拦截器中的形参

  • 请求拦截器的参数

    • config:即axios的config属性,在返回该对象时可以自定义进行处理

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      // 设置请求拦截器
      axios.interceptors.request.use(function (config) {
      console.log("请求拦截器 成功 - 1号");
      // 修改config的参数
      config.params = {
      a: 100
      };
      return config;
      }, function (error) {
      console.log("请求拦截器 失败 - 1号")
      return Promise.reject(error);
      });

      // 设置请求拦截器
      axios.interceptors.request.use(function (config) {
      console.log("请求拦截器 成功 - 2号");
      // 修改config的参数
      config.timeout = 2000;
      return config;
      }, function (error) {
      console.log("请求拦截器 失败 - 2号")
      return Promise.reject(error);
      });

      image-20220301150030718

  • 响应拦截器的参数

    • response:即then回调函数的形参,为响应信息,同样也可以进行自定义处理,例如只返回响应消息的响应体

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      // 设置响应拦截器
      axios.interceptors.response.use(function (response) {
      console.log("响应拦截器 成功 - 1号");
      // return response;
      // 只返回响应体的结果
      return response.data;
      }, function (error) {
      console.log("响应拦截器 失败 - 1号");
      return Promise.reject(error);
      });

2.2.9 取消请求

为防止axios发送请求后得到响应过快,可在json服务器端设置延迟响应,这样可观察到取消请求的功能。

1
json-server --watch db.json -d 2000 # 延迟2s响应

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 获取按钮
const btns = document.querySelectorAll("button");
// 2.声明一个全局变量
let cancel = null;

btns[0].onclick = function() {
axios({
method: "GET",
url: "http://localhost:3000/posts",
// 1.添加配置对象的属性
cancelToken: new axios.CancelToken(function(c) {
// 3.将c赋值给cancel
cancel = c;
})
}).then(response => {
console.log(response.data);
})
};

// 取消请求
btns[1].onclick = function() {
cancel();
};

image-20220301151248249

完善

用户多次点击发送请求,势必会给服务器带来处理压力,因此当用户多次点击发送请求时,检查上次请求是否已经得到响应,如果没有,则取消上次请求。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// 获取按钮
const btns = document.querySelectorAll("button");
// 2.声明一个全局变量
let cancel = null;

btns[0].onclick = function() {
// 检测上一次请求是否已经完成
if(cancel !== null) {
// 取消上一次的请求
cancel();
}

axios({
method: "GET",
url: "http://localhost:3000/posts",
// 1.添加配置对象的属性
cancelToken: new axios.CancelToken(function(c) {
// 3.将c赋值给cancel
cancel = c;
})
}).then(response => {
console.log(response.data);
// 请求响应时,cancel置空
cancel = null;
})
};

// 取消请求
btns[1].onclick = function() {
cancel();
};

image-20220301151900184

3 axios源码分析

3.1 axios文件结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
├── /dist/ # 项目输出目录
├── /lib/ # 项目源码目录
│ ├── /adapters/ # 定义请求的适配器 xhr、http
│ │ ├── http.js # 实现 http 适配器(包装 http 包)
│ │ └── xhr.js # 实现 xhr 适配器(包装 xhr 对象)
│ ├── /cancel/ # 定义取消功能
│ ├── /core/ # 一些核心功能
│ │ ├── Axios.js # axios 的核心主类
│ │ ├── dispatchRequest.js # 用来调用 http 请求适配器方法发送请求的函数
│ │ ├── InterceptorManager.js # 拦截器的管理器
│ │ └── settle.js # 根据 http 响应状态,改变 Promise 的状态
│ ├── /helpers/ # 一些辅助方法
│ ├── axios.js # 对外暴露接口
│ ├── defaults.js # axios 的默认配置
│ └── utils.js # 公用工具
├── package.json # 项目信息
├── index.d.ts # 配置 TypeScript 的声明文件
└── index.js # 入口文件