Browse Source

stop axios

master
jingxun 2 years ago
parent
commit
38894cd50b
  1. 1
      .gitignore
  2. BIN
      code/.DS_Store
  3. 19
      code/db.json
  4. 40
      code/index.html
  5. 116
      code/index00.html
  6. 301
      note/lesson00_start_axios.md
  7. 212
      note/lesson01_axios_config.md
  8. 186
      note/lesson02_axios_interceptors.md
  9. 91
      note/lesson03_axios_cancel.md
  10. 0
      note/lesson04_creation_process_of_axios.md

1
.gitignore

@ -1 +1,2 @@
*min*
.DS_Store

BIN
code/.DS_Store

Binary file not shown.

19
code/db.json

@ -0,0 +1,19 @@
{
"posts": [
{
"id": 1,
"title": "json-server",
"author": "typicode"
}
],
"comments": [
{
"id": 1,
"body": "some comment",
"postId": 1
}
],
"profile": {
"name": "typicode"
}
}

40
code/index.html

@ -0,0 +1,40 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<link rel="stylesheet" href="./css/bootstrap.min.css">
<script src="./js/axios.min.js"></script>
<style>
* {
padding-left: 10px;
}
</style>
</head>
<body>
<h2 class="page-header">Cancel request</h2>
<button class="btn btn-primary">Send request</button>
<button class="btn btn-warning">Cancel</button>
<script>
btns = document.querySelectorAll("button");
let cancel = null;
btns[0].onclick = () => {
if (cancel !== null) cancel();
axios({
method: "GET",
url: "http://localhost:3000/posts",
cancelToken: new axios.CancelToken(c => cancel = c)
}).then(response => {
console.log(response);
cancel = null;
});
}
btns[1].onclick = () => cancel();
</script>
</body>
</html>

116
code/index00.html

@ -0,0 +1,116 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<link rel="stylesheet" href="./css/bootstrap.min.css">
<style>
* {
margin: 0;
padding-left: 20px;
}
</style>
<script src="./js/axios.min.js"></script>
</head>
<body>
<h2 class="page-header">Use axios</h2>
<button class="btn btn-primary">GET posts</button>
<button class="btn btn-primary">GET comments</button>
<button class="btn btn-primary">GET profile</button>
<!-- <button class="btn btn-warning">POST request</button>
<button class="btn btn-succcess">PUT request</button>
<button class="btn btn-danger">DELETE request</button> -->
<script>
// let btns = document.querySelectorAll("button");
// const apis = axios.create({
// method: "GET",
// baseURL: "http://localhost:3000"
// });
// apis({
// url: "/posts"
// }).then(
// response => console.log(response.data)
// );
// axios.defaults.method = "GET";
axios.defaults.baseURL = "http://localhost:3000";
// btns[0].onclick = () => {
// axios({
// url: "/posts"
// }).then(
// response => console.log(response)
// );
// }
// btns[1].onclick = () => {
// axios({
// url: "/comments"
// }).then(
// response => console.log(response)
// );
// }
// btns[2].onclick = () => {
// axios({
// url: "/profile"
// }).then(
// response => console.log(response)
// );
// }
axios.interceptors.request.use(
config => {
console.log("请求拦截器成功 1");
// config.params = { id: 100 };
return config;
},
error => {
console.log("请求拦截器失败 1");
return Promise.reject(error);
});
// axios.interceptors.request.use(
// config => {
// console.log("请求拦截器成功 2");
// return config;
// },
// error => {
// console.log("请求拦截器失败 2");
// return Promise.reject(error);
// });
axios.interceptors.response.use(
response => {
console.log("响应拦截器成功 1");
console.log(response);
return response.data;
},
error => {
console.log("响应拦截器失败 1");
return Promise.reject(error);
});
// axios.interceptors.response.use(
// response => {
// console.log("响应拦截器成功 2");
// return response;
// },
// error => {
// console.log("响应拦截器失败 2");
// return Promise.reject(error);
// });
axios({
url: "/posts",
method: "GET"
}).then(
response => console.log(response)
).catch(
error => console.log("请求失败")
);
</script>
</body>
</html>

301
note/lesson00_start_axios.md

@ -0,0 +1,301 @@
# `Axios`介绍与基本使用
哈喽,好久不见,或许会有朋友在想这段时间我在干什么,但是看这个浏览量可能并没有,那么这段时间我在干什么呢?实不相瞒,我也在想这个问题。
## 序
从我这个网站系列教程从头看到现在的朋友可能知道我出了`React`全家桶教程,出了`Promise`教程,好像在此之前还有一点`Python`教程的前言部分。也许有朋友会疑问:你到底是前端还是后端呢?其实我现在虽然在公司里主要职责是后端,但是实际上我是一个全栈,或许大家也看见了我置顶的简历,但是那已经快要两年都没有更新了,那么这将近两年的时间在我身上都发生了什么呢?
我在这两年时间一直是不安现状的,我做了`Go`语言开发,`Java`开发,做了`GUI`开发,重新学习了`C`语言,尝试了`C++`,利用`Rust`来重构过一些小项目,还尝试了用`C`语言和`C++`以及`Rust`语言来自己实现`Python`底层。我也紧跟过行业的最前沿,也关注过最新的技术栈,也对比过主流编程语言的设计哲学之间的异同以及各自的优势,研究过各语言底层的内存分配。而且单独拎出来一段时间重新研究汇编语言,所谓温故而知新,虽然我还不能像一些大牛那样能用汇编来写一个编译器,但对我的`C`语言还是有大的帮助。
除了这些尝试去接触和学习关于架构层面的东西。我在入行之前听说过架构师这个词,那时候还不知道架构师是个什么,但是却也立下誓愿:我是要成为架构师的男人。现如今入行 5 年多,初心未改。
## 概述
当然我写这些确实和今天这节课的内容没有任何关系,我是什么时候开始想起来写这些教程的呢?因为我入职目前的公司之后就基本在做后端开发,久而久之前端方面的知识都渐渐开始遗忘了,所以我就干脆也别说复习了,直接重新以一个初学者的心态重新学习一下吧,当然了,我没有写`HTML/CSS/JavaScript`的教程,而是从一个综合性比较强的前端框架开始。
但是我们现在市面上主流的前端框架都是只管页面展示,不管向后端发送请求的环节,那么开发中的`ajax`请求那就得我们自己手动发送,但是呢我没有出`ajax`请求的教程,惊不惊喜,意不意外?因为实在是太简单了,没有什么可说的,我们也都知道前端发送`ajax`请求可以通过原生`js`来实现`ajax`请求,也可以通过`jQuery`封装的`API`来发送`ajax`请求,但是现在市面上的前端开发可以说是大框架时代,最主流的两大框架就是`React`和`Vue`,可以说基本上不会有人在这两个框架里用`jQuery`的,而且原生的`ajax`请求也太繁琐,所以市面上基本都在最主流的一个发送`ajax`请求的库,这就是我们接下来的主角:`axios`。
但是我们为什么要用`axios`呢?虽然说原生的`ajax`比较繁琐,但是`axios`就没有其他的过人之处了吗?当然不是:
- `axios`是比较轻量级的一个库
- 封装`XmlHttpRequest`对象的`ajax`
- `Promise`风格
- 可以用在浏览器和`node`服务端
当然了在学习`axios`之前还是有两个前置知识要掌握的:
- `ajax`
- `Promise`
是不是没想到,我之前出的`Promise`教程居然在这里用上了,但是没有`ajax`教程,不重要,因为原生`ajax`的流程是非常简单的:
- 创建`XMLHttpRequest`对象
- 配置请求链接以及请求方式
- 发送请求
- 配置`onreadystatechange`函数,在函数中判断`readyState`,如果`readyState === 4`就继续判断请求状态码,然后根据状态码进行后续操作
这就是整个原生`ajax`的流程,至于里面还有什么其他的配置,在后续的内容中我们也会提到。
## `json-server`
在正式开始学习`axios`之前呢,我们先来看一个库,这是什么呢?
![image-20220317105711636](https://file.lynchow.com/2022-03-17-025713.png)
因为`axios`是一个发送`ajax`请求的库,那么我们后面学习`axios`肯定是要向接口发送请求的,但是我们可没有现成的`demo`接口供我们请求啊,而`json-server`这个库的作用就是快速地给我们提供一个用于测试的接口。那么我们来看一下怎么用?
![image-20220317110030359](https://file.lynchow.com/2022-03-17-030033.png)
官方文档已经给我列出来了,首先第一步肯定是要来安装这个库:
```shell
npm install -g json-server
```
然后我们要创建一个`json`文件叫`db.json`,从文件名上大家也能猜出来,这个文件是用来充当数据库的,那么我们就直接用官方给我们提供的样例吧:
```json
{
"posts": [
{ "id": 1, "title": "json-server", "author": "typicode" }
],
"comments": [
{ "id": 1, "body": "some comment", "postId": 1 }
],
"profile": { "name": "typicode" }
}
```
当我们创建好这个数据文件之后我们看到文档中提示已经可以启动服务了,我们在终端中来执行命令:
```shell
json-server --watch db.json
```
但是有一点一定要注意,这个命令一定要在`db.json`文件的同级目录来执行,否则会报错说找不到`db.json`文件的。
![image-20220317112412500](https://file.lynchow.com/2022-03-17-032415.png)
我们看见我们执行完命令之后终端里面输出相关信息,并且提供了几个链接,我们可以通过请求这些链接来获取到`db.json`文件中的信息:
![image-20220317113051211](https://file.lynchow.com/2022-03-17-033053.png)
那么,我们到这一步便开启了一个我们用于测试的接口供我们后期使用。以上便是`json-server`库的基本使用。
## `axios`的介绍
我们现在基本是把我们的准备工作都做好了,那么现在我们正式开始`axios`的学习,首先第一个问题,`axios`是什么?
![image-20220317131121137](https://file.lynchow.com/2022-03-17-051122.png)
我们来看一下,官方给的介绍说`axios`是一个基于`Promise`的可以同时运用在浏览器和`node.js`服务端的一个`HTTP`客户端,这是什么意思呢?就是我们可以用`axios`在浏览器中或者`node.js`的服务端来发送`HTTP`请求。而且发送请求所得到的结果是一个`Promise`对象。
![image-20220317131457538](https://file.lynchow.com/2022-03-17-051458.png)
那么我们知道`axios`是用来干什么的了,我们来看一下`axios`都有哪些特点呢:
- 能从浏览器端发送`ajax`请求
- 能在`node.js`服务端发送`HTTP`请求
- 支持`Promise`对象的相关操作
- 可以拦截请求和响应
- 可以完成请求和响应之间的数据转换
- 可以取消请求
- 可以自动将数据转为`json`
- 可以防止`XSRF`攻击。
那么我们怎么来使用`axios`呢?首先肯定是安装`axios`,官方提供了很多种方法,包括用`npm`或者`yarn`来安装还有通过`script`标签来引入`CDN`,通常我们在项目开发中都会通过`npm`或者`yarn`来安装`axios`,但是我们在学习时候我们就通过`script`标签来引入吧,效果是一样的:
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="./js/axios.min.js"></script>
</head>
<body>
<script>
console.log(axios);
</script>
</body>
</html>
```
我们来看代码,我想大家如果看过我们写的`react`教程的话,都记得在刚开始引入`react`的时候我们说过,一旦我们引入了`react`,那么全局就会多出来一个全局变量`React`,那么`axios`也一样,我们引入`axios`,然后我们打印一下`axios`看一下结果:
![image-20220317133041991](https://file.lynchow.com/2022-03-17-053047.png)
我们看到控制台中输出了一个函数,这就说明了我们成功引入了`axios`。
## `axios`的基本使用
我们前面已经介绍了如何引入`axios`,那么接下来我们就开始使用吧,首先我们先写一个`demo`:
```html
<h2 class="page-header">Use axios</h2>
<button class="btn btn-primary">GET request</button>
<button class="btn btn-warning">POST request</button>
<button class="btn btn-succcess">PUT request</button>
<button class="btn btn-danger">DELETE request</button>
```
我们来看一下代码,我们先是一个标题,然后加了 4 个按钮,当然我们这里引入了`bootstrap`来快速给页面来加一个样式,我们看一下静态页面的效果:
![image-20220317135251112](https://file.lynchow.com/2022-03-17-055252.png)
当然我们现在还没有给按钮加`onclick`事件,那么我们接下来先把我们的测试接口启动起来,还是之前的`json-server`,我们来看一下`json-server`给我提供的这几个接口的用法:
![image-20220317135654232](https://file.lynchow.com/2022-03-17-055655.png)
我不知道大家有没有自己去尝试请求过这几个`URL`路径,这是什么意思呢?
- 第一个就是简单的获取`db.json`中`posts`字段所有数据
- 第二个是获取`posts`字段里面`id`为 1 的那一条结果
- 第三个是在`posts`字段里面新增数据
- 第四个是更新`posts`字段中`id`为 1 的那一条数据
- 接下来`PATCH`请求我们好像没怎么用过,这个请求方式和`PUT`类似,也是更新数据,大家可以去了解一下,这里不做赘述了
- 最后一个是删除`posts`字段中`id`为 1 的数据
那么我们准备好了接口,也知道了大概用法,那么我们就给按钮添加`onclick`事件来发送`ajax`请求吧:
```js
let btns = document.querySelectorAll("button");
btns[0].onclick = () => {
axios({
method: "GET",
url: "http://127.0.0.1:3000/posts"
}).then(
response => console.log(response)
);
}
```
我们来看一下代码,首先我们是获取我们的按钮,那么我们来给第一个按钮设置一下`onclick`事件的回调,我们是发送一个`ajax`请求,而且是用`axios`来发送,那么就直接调用`axios`函数,函数接收一个对象,这个对象中我们可以来设置请求方式和请求地址,我们说过,`axios`的返回结果是一个`Promise`对象,那么既然是`Promise`那么就能调用`then`方法,我们来指定一下成功的回调,并把响应体输出到控制台:
![image-20220317141412365](https://file.lynchow.com/2022-03-17-061414.png)
我们看见控制台中输出了这次网络请求的`response`,我们这里看响应结果有些太繁琐了,里面把相关的配置请求头,响应状态都包含在里面了,我们直接去`network`里面去看一下:
![image-20220317141615230](https://file.lynchow.com/2022-03-17-061616.png)
我们看见这里的`Response`就和我们`db.json`里面的数据是一致的。那么我们再来看一下`POST`请求:
```js
btns[1].onclick = () => {
axios({
method: "POST",
url: "http://127.0.0.1:3000/posts",
data: {
title: "Test title",
author: "Jingxun"
}
}).then(
response => console.log(response)
);
}
```
我们来看一下,还是调用`axios`函数,这次指定的`method`改成了`POST`,而且还有一点,我们`POST`请求是要带上请求体的,我们说过`POST`方法是在`db.json`里面添加一条数据,那么我们就要把数据放在请求体里面,我们来看`data`属性,是一个对象,这个对象里面只要添加`title`和`author`属性就行了,`id`属性是`json-server`自行添加的一个主键,那么我们来看一下这次请求是什么效果。
![image-20220317142557464](https://file.lynchow.com/2022-03-17-062559.png)
我们成功添加了这条数据到`db.json`文件中。那么我们再来试一下`PUT`请求:
```js
btns[2].onclick = () => {
axios({
method: "PUT",
url: "http://127.0.0.1:3000/posts/2",
data: {
title: "New Title",
author: "Jingxun"
}
}).then(
response => console.log(response)
);
}
```
我们这次尝试把之前添加进去的数据修改一下,把`title`属性改成`New Title`,那么我们把请求方式改成`PUT`,这次`url`也要改,因为我们要把我们要更新的`id`也传进去,我们在来卡一下结果:
![image-20220317145425974](https://file.lynchow.com/2022-03-17-065428.png)
这次我们成功把`title`属性修改了,那么我们再来试一下`DELETE`请求:
```js
btns[3].onclick = () => {
axios({
method: "DELETE",
url: "http://127.0.0.1:3000/posts/2"
}).then(
response => console.log(response)
);
}
```
这次我们不需要添加请求体了,只要请求方式和`url`就可以了,但是要把我们要删除的`id`传进来,那么我们来试一下能不成功删除`id`为 2 的数据:
![image-20220317150138638](https://file.lynchow.com/2022-03-17-070140.png)
我们看见这次`posts`字段中就只有一条数据了。
以上便是`axios`的几种常用请求的基本使用方法。
## `axios`其他方式发送请求
当然了,`axios`还有一些其他的`API`供我们来发送`ajax`请求,都有哪些呢:
![image-20220317151113569](https://file.lynchow.com/2022-03-17-071115.png)
官方给我提供了很多方法,有`request`方法,这个方法和直接调用`axios`函数没有任何区别,也是接收一个对象,在对象里进行相关的配置。
其次还有一些封装好的`HTTP`请求方法,比如`get`方法,`post`方法等,如果我们调用`get`方法,那么`axios`就会直接发送一个`get`请求,其他的方法也都是一样。我们看见想这种`HTTP`方法中接收的形参列表中有这种`[, config]`参数,这是什么意思呢?这些用`[]`括起来的代表着是可选参数,可以不传。
以上便是`axios`的其他发送`ajax`请求的方法,大家可以自己尝试一下。
## `axios`响应结果的结构
我们现在已经可以通过`axios`发送`ajax`请求了,但是我不知道大家还能不能记得上面有一段说在控制台中看我们的`response`里面的结构很复杂,那么我们就来说一下这个里面的结构都是什么:
```js
let btns = document.querySelectorAll("button");
btns[0].onclick = () => {
axios({
method: "GET",
url: "http://127.0.0.1:3000/posts"
}).then(
response => console.log(response)
);
}
```
我们还是用最开始的`GET`请求的案例来看一下:
![image-20220317152203568](https://file.lynchow.com/2022-03-17-072205.png)
首先我们揽件控制台里面输出一个对象,首先我们看看这个对象里面有哪些属性:
- `config`配置对象
- `data`响应体结果
- `headers`响应头信息
- `request`原生`ajax`对象
- `status`响应状态码
- `statusText`响应状态字符串
这些属性分别都是什么呢?首先配置对象,这个里面就是我们在发送请求时候做的一些配置,比如我们的请求方式啊,请求的链接啊这一类的信息都会存放在`config`属性里。
然后响应体结果,这个就是后端接口返回给前端的数据,也就是我们真正在前端开发中需要展示到页面上的数据,我们看待这是个数组对象,这就是`axios`的一个优势了,可以把结果自动进行`json`解析生成一个对象。
接下来是响应头信息,我们都知道响应包含四个部分,响应头,响应行,响应空行和响应体,这里就是我们的一个响应头信息。
再接着就是`ajax`对象,这个是什么呢?我们的`axios`发送的是`ajax`请求,不论你一个库封装地再怎么好,只要发送`ajax`请求那么底层一定是要用`XMLHttpRequest`对象的,而这里的`request`属性就是当前`ajax`请求创建的`XMLHttpRequest`对象。
至于`status`和`statusText`我们就没有什么可说的了,这两个就代表着我们的请求状态,是成功了还是失败了,或者是重定向了之类的。
以上就是`axios`请求响应结果的结构的大致介绍。
当然`axios`也并不是就这么简单的几下还有很多其他的配置项以及用法原理我们会在后续内容中逐一介绍。这节课我们就先到这里。

212
note/lesson01_axios_config.md

@ -0,0 +1,212 @@
# `Axios`配置对象
上节课我们初步介绍了一下`axios`以及`axios`的一些基本用法,而且对`axios`的响应结果的结构做了一个简单的介绍。但是呢我们有一个很重要的东西,但是上节课实际上介绍的并不多,就是我们的配置对象,那么这节课我们来对这个配置对象来做一个介绍。
## `axios`配置对象常用属性
首先我们打开官方文档,来看一下`axios`的配置对象里面都有什么属性:
```js
{
url: '/user',
method: 'get',
baseURL: 'https://some-domain.com/api/',
transformRequest: [function (data, headers) {
return data;
}],
transformResponse: [function (data) {
return data;
}],
headers: {'X-Requested-With': 'XMLHttpRequest'},
params: {
ID: 12345
},
paramsSerializer: function (params) {
return Qs.stringify(params, {arrayFormat: 'brackets'})
},
data: {
firstName: 'Fred'
},
data: 'Country=Brasil&City=Belo Horizonte',
timeout: 1000,
withCredentials: false,
adapter: function (config) {},
auth: {
username: 'janedoe',
password: 's00pers3cret'
},
responseType: 'json',
responseEncoding: 'utf8',
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
onUploadProgress: function (progressEvent) {},
onDownloadProgress: function (progressEvent) {},
maxContentLength: 2000,
maxBodyLength: 2000,
validateStatus: function (status) {
return status >= 200 && status < 300;
},
maxRedirects: 21,
beforeRedirect: (options, { headers }) => {
if (options.hostname === "example.com") {
options.auth = "user:password";
}
};
socketPath: null,
httpAgent: new http.Agent({ keepAlive: true }),
httpsAgent: new https.Agent({ keepAlive: true }),
proxy: {
protocol: 'https',
host: '127.0.0.1',
port: 9000,
auth: {
username: 'mikeymike',
password: 'rapunz3l'
}
},
cancelToken: new CancelToken(function (cancel) {}),
signal: new AbortController().signal,
decompress: true
}
```
这么多属性,这我得一个个说到什么时候去啊。。。。所以我们就对我们常用的几个属性来做一个介绍吧,至于这些不常用的属性,大家简单做个了解就好,如果有兴趣的可以自己尝试去测试一下。
那么我们来开始介绍一下这些属性,在这么多属性中,官方给的提示是,`url`属性是必须配置的,其他的属性都是可选的,因为需要通过配置`url`属性来明确请求链接。这里的`url`属性可以是一个路径,也可以是一个完整的链接。但是如果写的是一个路径,那么我们就一定要配置`baseURL`属性,我们上节课中在配置请求的时候,`url`中写了完整的链接,但是我们是可以把`http://127.0.0.1:3000`来作为`baseURL`属性来配置的,这样的话我们的`url`属性直接配置为后面的路径就好了,在发送请求的时候`axios`会根据配置对象来讲`url`属性和`baseURL`属性相结合生成一个完整的链接。
再看`method`属性,这个属性是用来配置请求方式的,但是这个属性是可选的,如果我们不对`method`进行配置的话,默认就会发送`GET`请求,而且我们也从官方文档中看到了,`method`属性不用区分大小写。
接下来两个属性`transformRequest`,`transformResponse`这两个属性呢是我们自定义的回调函数,也是可选的,`transformRequest`属性里的回调可以对请求参数做一个预处理,然后将处理后的数据发送给后端接口,而`transformResponse`恰恰相反,他是对响应结果来做预处理的回调,所以说这两个属性就是对请求参数和响应结果做预处理的。
然后是一个很常用而且很实用的属性,就是`headers`,这是一个请求头信息属性,有些时候前端向后端请求的时候会要求带一个`token`,从而来验证请求来源,通常来说我们的`token`都会放在`headers`里面。
再往后看`params`属性,这也是一个很常用的属性这个属性是个对象,用来设定`url`参数的。我们经常会看见`http://host:port/path?a=1&b=2`这种链接,在这个链接的问号后面就是`url`参数,当然我们把这些写在`url`属性中自然是没有问题的,但是如果`url`参数比较多的话这么写还是不方便的,所以说我们可以借助`params`属性来配置这些`url`参数。
然后下面这个`paramsSerializer`参数我们很少用,这是一个把我们请求参数序列化转换成一个字符串的函数,简单做个了解就好,就不赘述了。
接下来是一个很重要的属性:`data`属性,这个就是请求体属性,但是我们看见这里写了两次`data`属性,这里不是语法错误啊,这是代表了`data`属性有两种配置方式,第一种是对象形式,如果我们用对象形式配置请求体,在发送请求时就会把`data`属性转为一个`json`字符串来传给后端接口。另一种形式则是表单形式,有点类似`url`参数,以键值对字符串的形式传递。
再下面,`timeout`属性,设置超时时间,单位为毫秒,一旦超过这个请求时间则取消请求。这个我们是比较常用的一个属性
然后`withCredentials`属性是对跨域请求是否携带`cookies`做了一个限制,`false`则表示不携带。
后面`adapter`属性,是对请求的识别器做一个设置,有两种,一种是发送`ajax`的,另一种是`node.js`中发送`http`请求的两个运行环境。
再后面`auth`属性,这是我们请求基础做验证来设置用户名和密码的,这个属性也基本很少用。
再来看`responseType`属性,这个是对响应体结果的格式做一个设置,默认是`json`。
然后`responseEncoding`属性是响应体结果的一个编码,默认是`utf8`这是一个字符集设置。
接下来`xsrfCookieName`和`xsrfHeaderName`属性,这两个是对跨站请求携带的`cookies`的名字以及请求头的信息来做一个设置,这是一个安全设置确保这个请求是来自我们的自己的客户端,而不是一下不明确的来源。是一个保护作用。
再后面`onUploadProgress`和`onDownloadProgress`属性这两个是用来设置上传和下载的回调的。
接着我们来看`maxContentLength`和`maxBodyLength`属性,这是设置`http`响应体和请求体的一个最大尺寸,单位是字节
再下面`validateStatus`属性,这是对响应结果的成功来做一个设置,当然我们很少用,除非后端自定义了什么特殊的状态码。
再接着就是`maxRedirects`属性,这是设置最大跳转次数的一个属性,就是比如我们请求了一个接口,这个接口让我们的请求进行跳转,那么我们就进行跳转,跳转之后让我们再次跳转,那么我们就继续跳转,但是跳到什么时候才是个头儿呢?我们就可以用这个属性来规定最多跳转多少次我们就不再进一步跳转了。但是在前端`ajax`请求中我们基本用不到这个属性。
然后是`beforeRedirect`属性,这个属性就很见名知意了,在跳转前执行的回调。
然后是`socketPath`这个是用来设置`socket`文件的路径的,这个是一个数据转发的机制,简而言之就是代理,会和`proxy`属性有一个优先级的问题,如果二者同时存在,那么就优先走`socket`。
然后`httpAgent`和`httpsAgent`这是对`HTTP`客户端信息来做一个设置,比如设置是否保持连接状态之类的。这两个属性也是很少用的。
然后`proxy`属性,这是用来配置代理的,通常在爬虫中用得比较多,前端`ajax`请求一般不太常用。
接下来是`cancelToken`这个属性是对请求取消来做一个设置,后面的学习中我们会再做介绍。
下面有一个`signal`属性,这是另一种取消`axios`发送请求的方法。
再下面是`decompress`属性,这个是设置是否对响应结果解压,但是在`ajax`请求中是无法使用这个属性的。
以上便是我们对`axios`配置对象中各属性地一个介绍,当然比较常用的属性我们的内容说得都比较多,不常用的属性我们也就只是简单说了一下,大家简单做个了解就可以了。
## `axios`默认配置
我们前面对`axios`的配置对象做了一个大概的介绍,接下来我们来介绍一下`axios`的默认配置。`axios`的默认配置是一个非常使用的一个技巧。我们配置对象中的所有属性都可以在`axios`的默认配置中来配置。比如说我们要请求这样三个`url`:
- `http://localhost:3000/posts`
- `http://localhost:3000/comments`
- `http://localhost:3000/profile`
而且对这三个`url`发送的都是`GET`请求,那么我们怎么写呢?
```js
axios({
method: "GET",
url:"http://localhost:3000/posts"
}).then(...);
axios({
method: "GET",
url:"http://localhost:3000/comments"
}).then(...);
axios({
method: "GET",
url:"http://localhost:3000/profile"
}).then(...);
```
首先来说,这样写行不行?当然是可以的对吧,但是有没有觉得这样写太麻烦了,我们每次请求里面都要写`method`属性,每次请求`url`都要写那么长一串,是不是很烦?那么我们来改一下代码:
```js
axios.defaults.method = "GET";
axios.defaults.baseURL = "http://localhost:3000";
axios({
url:"/posts"
}).then(...);
axios({
url:"/comments"
}).then(...);
axios({
url:"/profile"
}).then(...);
```
我们来看一下这段代码我们设置了默认请求方式,而且设置了默认的`baseURL`,那么我们来看一下结果:
![image-20220318082717291](https://file.lynchow.com/2022-03-18-002720.png)
我们发现我们所发送的这些请求都是成功了的,但是整体上我们的代码要比之前要简洁很多,以上便是我们`axios`的默认配置的基本用法的简单介绍。
## `axios`实例对象发送请求
我们介绍完了配置对象和默认配置之后,我们来介绍一个新的内容,就是用`axios`实例对象来发送请求。
那么首先我们要怎么去创建一个`axios`的实例对象呢?
![image-20220318083548290](https://file.lynchow.com/2022-03-18-003549.png)
官方文档也给出了相应的方法,我们可以通过`axios`自身的`create`方法来创建一个`axios`的实例对象。我们来看一下代码:
```js
const apis = axios.create({
method: "GET",
baseURL: "http://localhost:3000"
});
apis({
url: "/posts"
}).then(
response => console.log(response.data)
);
```
我们用`create`方法创建了一个`axios`的实例对象,这个`create`方法要接收一个参数,这个参数就是我们的配置对象,相当于对这个`axios`实例对象做了一个默认配置,我们看里面我设置了默认请求方法和默认的`baseURL`。
其实这里我们创建的实例对象和`axios`在功能上几乎是一样的,但是不是完全一样,等我们介绍到源码的时候再来说明这一部分。既然功能几乎一样,那么我们就可以像`axios`那样发送请求,我们来请求`posts`路径,但是我们这次就不直接输出`response`了,我们这次输出响应体内容,也就是`response`的`data`属性。我们来看一下请求结果:
![image-20220318084533198](https://file.lynchow.com/2022-03-18-004534.png)
我们可以看到控制台上已经成功输出了我接口响应的结果了,但我们为什么要用这种方式呢?`axios`可以直接发送请求,也可以直接设置默认的配置,为什么还要创建实例对象来发送请求呢?我们有些情况下前端所要请求的接口服务并不只有一个,或许一个项目中要请求多个接口服务,`axios`虽然可以设置默认的配置,但是如果我们要请求多个接口服务的话,每个接口服务的协议和域名包括端口都不一样,我们直接通过`axios`来配置默认配置的话只能配置其中一个接口服务的默认配置,那么其他的我们还是要在每一个请求中来做一些相关的重复配置,这样也还是会不方便。
所以说我们可以通过`axios`来创建实例对象,每个接口服务都是一个新的实例对象,相互独立,互不干扰,而且还可以独立配置各自的默认配置,这样的话就可以有效地提高我们的开发效率。
以上就是`axios`实例对象发送请求的基本用法以及应用场景的介绍。

186
note/lesson02_axios_interceptors.md

@ -0,0 +1,186 @@
# `Axios`拦截器
上节课我们介绍了`axios`的配置对象以及`axios`实例对象发送请求做了一个基本的介绍,这节课呢,我们来介绍一个`axios`中非常重要的一个功能:拦截器。
## 什么是拦截器
我们刚接触到一个新名词的时候都会觉得陌生,好奇,这个名词是什么意思,这个名字所代指的东西到底是什么?那么现在我们不禁要问,什么是拦截器呢?这个拦截器的作用又是什么呢?
首先来说,拦截器从本质上来说就是一个个的函数,`axios`中的拦截器分成两大类,一种是请求拦截器,另一种是响应拦截器,但是这个拦截器到底实质上的意义是什么呢?
通俗点说的话这里的拦截器有点类似我们坐地铁时要过的安检系统,当我们去坐地铁时,要过安检,安检没有问题就放行我们就可以去正常去乘坐地铁,安检如果有问题,那么就把你拦截住,不允许乘坐地铁。这就很像我们这里要说的请求拦截器。
在`axios`发送请求之前,请求拦截器会对请求做一个预处理,如果请求预处理没有问题就发送请求,否则就取消请求。
当然我们刚才说的地铁安检只有进站安检,没有出站安检,那么我们的响应拦截器可就不能用安检系统来举例了,但是也差不多,请求拦截器就是先把请求的响应结果拦截下来做一系列的预处理,如果响应结果预处理没有问题就放行交给我们请求成功后的回调来处理,如果响应结果有问题,那么就在拦截器中通过统一的回调来处理。
## 拦截器的使用
光这么说好像也不是太容易理解,那么我们用代码来介绍一下:
```js
axios.interceptors.request.use(
config => {
console.log("请求拦截器成功");
return config;
},
error => {
console.log("请求拦截器失败");
return Promise.reject(error);
});
axios.interceptors.response.use(
response => {
console.log("响应拦截器成功");
return response;
},
error => {
console.log("响应拦截器失败");
return Promise.reject(error);
});
axios({
url:"/posts",
method:"GET"
}).then(
response => console.log("用户自定义回调")
);
```
我们来看一下代码,首先我们来看最外层结构,我们添加了两个拦截器,一个是`request.use`另一个是`response.use`,这两个方法是用于创建请求以及响应拦截器的方法,然后我们看每个`use`方法中都接收了两个函数作为参数,这两个参数分别是成功时和失败时的回调函数,所以说大家有没有觉得很熟悉?没错,这就是通过`Promise`的`then`方法来实现的。所以说这一块要运用到我们的`Promise`的知识了。如果大家对`Promise`的知识掌握的不好的话,对这里的流程理解起来就会有点吃力。
现在我们有了两个拦截器了,那么我们来发送请求,首先我们来判断一下,这个请求发送的流程是什么?我们这个请求在之前没有拦截器之前也发送过,一直都是成功的,所以说目前来说没有失败的情况。那么在发送请求之前是不是首先要经过请求拦截器?走请求拦截器中成功的回调?然后请求拦截器放行,接口返回响应结果,经过响应拦截器,走成功的回调,放行交给我们用户在`then`方法中自定义的回调?那么我们来看一下结果:
![image-20220318094211020](https://file.lynchow.com/2022-03-18-014212.png)
我们看见流程和我们预期的一样。那么如果我们让请求拦截器中抛出一个异常:
```js
axios.interceptors.request.use(
config => {
console.log("请求拦截器成功");
throw "error";
},
error => {
console.log("请求拦截器失败");
return Promise.reject(error);
});
```
我们来看一下结果:
![image-20220318100910448](/Users/jingxunqian/Library/Application Support/typora-user-images/image-20220318100910448.png)
我们来看一下,因为成功的回调中抛出了异常,那么拦截器就会返回一个失败的`Promise`对象,但是在抛出异常之前的代码还是会正常输出的,但是我们要注意的一点就是,我们请求拦截器返回了一个失败的`Promise`对象,那么响应拦截器就不可能成功了,所以说也会来走失败的回调,那么我们`then`方法中自定义的回调也就不会走成功的回调了。所以就会是以上的结果。
我们接下来再来说另一个问题:
```js
axios.interceptors.request.use(
config => {
console.log("请求拦截器成功 1");
return config;
},
error => {
console.log("请求拦截器失败 1");
return Promise.reject(error);
});
axios.interceptors.request.use(
config => {
console.log("请求拦截器成功 2");
return config;
},
error => {
console.log("请求拦截器失败 2");
return Promise.reject(error);
});
axios.interceptors.response.use(
response => {
console.log("响应拦截器成功 1");
return response;
},
error => {
console.log("响应拦截器失败 1");
return Promise.reject(error);
});
axios.interceptors.response.use(
response => {
console.log("响应拦截器成功 2");
return response;
},
error => {
console.log("响应拦截器失败 2");
return Promise.reject(error);
});
```
我们来看一下这段代码,我们设置了两个请求拦截器和两个响应拦截器,那么我们这时候发送请求流程会怎么样呢?按照我们常规思路应该是先走请求拦截器 1,然后是请求拦截器 2,接着通过响应拦截器 1 再通过响应拦截器 2,最后到我们用户的自定义回调。那么我们看一下结果是不是和我们预期的一样呢:
![image-20220318101810700](https://file.lynchow.com/2022-03-18-021812.png)
结果和我们想的好像不太一样,我们现在先提一下这个现象,等我们后面分析源码的时候再详细介绍为什么这里请求拦截器先进后出。
## 拦截器参数
接下来我们来介绍一下关于拦截器这里面指定的回调,这里我们都接收了参数,不过失败的回调的参数也就没必要说了,我们主要看成功的回调的参数。
请求拦截器接收了`config`参数,这个`config`就是我们的配置对象,也就是说我们在请求拦截器中来对请求的配置对象来做一个处理的,比如我在拦截器里面给`config`配置了`params`属性:
```js
axios.interceptors.request.use(
config => {
console.log("请求拦截器成功 1");
config.params = { id: 100 };
return config;
},
error => {
console.log("请求拦截器失败 1");
return Promise.reject(error);
});
```
那么我们发送请求会不会吧这个`url`参数带上呢?我们来看一下:
![image-20220318102608657](https://file.lynchow.com/2022-03-18-022610.png)
我们在`Network`中可以看见我们的`url`中已经带上了我们的`id`参数。
我们再来看一下响应拦截器成功回调的参数,`response`,这个其实`axios`自己构造的一个返回结果,`axios`会将接口服务返回的响应结果进一步做一个解析与打包。那么我们可以在回调中来做一个处理:
```js
axios.interceptors.response.use(
response => {
console.log("响应拦截器成功 1");
console.log(response);
return response.data;
},
error => {
console.log("响应拦截器失败 1");
return Promise.reject(error);
});
axios({
url: "/posts",
method: "GET"
}).then(
response => console.log(response)
).catch(
error => console.log("请求失败")
);
```
我们来看一下代码,我们先输出一下响应拦截器接收到的`response`是什么,然后我们返回`response`中的响应体内容交给我们自定义的回调,因为我们自定义的回调是不需要其他内容的,我们只要响应体内容就行了。那么我们来看一下输出:
![image-20220318103522998](https://file.lynchow.com/2022-03-18-023524.png)
我们看见响应拦截器中的`response`的结果还是很复杂的,里面的结构我们在之前的内容中介绍过,我就不重复了,但是我们自己定义的回调不需要那么多信息,我们只需要`data`属性就可以了所以我们让回调返回`response`的`data`属性。想这种类似的操作都可以放在响应拦截器中来实现。
以上便是拦截器的基本使用,我们将在后期源码分析中再做详细介绍。

91
note/lesson03_axios_cancel.md

@ -0,0 +1,91 @@
# `Axios`取消请求
上节课我们介绍了关于拦截器的基本用法,可能大家还是会有点迷糊,因为我们并没有介绍这方面的原理,其实我在写的时候也是很别扭,但是不要着急,多思考,等到我们后面分析源码的时候会详细介绍各个功能的原理,并且自己去实现这些功能。
## 取消请求
那么这节课我们开始学习一个新的内容,就是取消请求。首先我们来看一下页面:
![image-20220321134210826](https://file.lynchow.com/2022-03-21-054212.png)
我们现在页面上有两个按钮,一个是用来发送请求的,一个是用来取消请求的,我们先来实现一下发送请求:
```js
btns = document.querySelectorAll("button");
btns[0].onclick = () => {
axios({
method: "GET",
url: "http://localhost:3000/posts",
}).then(response => {
console.log(response);
});
}
```
这是我们很熟悉的流程了,首先找到按钮,然后绑定点击事件,在点击事件中通过`axios`来发送请求,我们来看一下效果:
![image-20220321134724520](https://file.lynchow.com/2022-03-21-054726.png)
我们看到我们现在是可以实现点击发送请求按钮来发送我们的网络请求的,那么我们怎么样才能实现取消请求呢?`axios`自然也是给我们提供了相关的功能。我们的配置对象里面是有一个属性叫`cancelToken`,那么我们来看一下应该怎么用:
![image-20220321135157793](https://file.lynchow.com/2022-03-21-055159.png)
官方文档给了我们一个样例,我们通过`axios`的`CancelToken`方法来创建一个新的函数,然后通过那个函数来取消请求,那么我们来实现一下:
```js
let cancel = null;
btns[0].onclick = () => {
axios({
method: "GET",
url: "http://localhost:3000/posts",
cancelToken: new axios.CancelToken(c => cancel = c)
}).then(response => {
console.log(response);
});
}
btns[1].onclick = () => cancel();
```
首先我们在全局声明了一个变量,这个变量用来接收我们创建出来的用于取消请求的`token`函数,但是我们为什么要在全局申请呢?因为我们是要在另一个按钮中的`onclick`事件中来取消请求的。那么好,我们现在可以来给取消按钮绑定`onclick`事件了,这个按钮就很简单了,只要在回调中调用一下`cancel`函数就可以了。
那么根据官方文档给的提示,我们在发送请求按钮的`onclick`事件中给`axios`的配置对象加上`cancelToken`属性就可以了,而这个属性的属性值就是用`axios.CancelToken`来创建的一个函数对象。那么我们来看一下效果:
![iShot2022-03-21 13.32.43](https://file.lynchow.com/2022-03-21-053312.gif)
我们好像点击取消的按钮根本都没有效果。因为我们向服务端发送了请求,还没等到我们 点击取消按钮,请求就已经完成了,所以说我们不可能去取消一个一完成的网络请求的。那么怎么办呢?我们的测试接口是用`json-server`开起来的,那么我们来看一下这个服务的一些扩展用法:
![image-20220321141125035](https://file.lynchow.com/2022-03-21-061126.png)
我们来看这个`-d`参数,我们可以用这个参数来让接口延迟响应,那么我们让接口延迟 3 秒再响应,这样我们就有足够的时间去点击取消按钮,那么现在我们再来看一下效果:
![iShot2022-03-21 14.17.46](https://file.lynchow.com/2022-03-21-061812.gif)
这一次我们看见我们已经实现通过点击取消按钮来取消请求。
那么我们来想一下,如果我们多次点击了发送请求的按钮,会是什么样子?是不是我们每点一次就会发送一个请求?如果说有恶意用户一直在点击发送请求的按钮,那么我们的服务器是不是会压力很大?那么我们通常会去检查上一个请求是否完成,如果没有完成就取消掉上一个请求,然后开启当前请求。但我们怎么实现呢?
```js
btns[0].onclick = () => {
if (cancel !== null) cancel();
axios({
method: "GET",
url: "http://localhost:3000/posts",
cancelToken: new axios.CancelToken(c => cancel = c)
}).then(response => {
console.log(response);
cancel = null;
});
}
```
我们来看一下代码,我们在`axios`发送请求之前做了一个判断。判断`cancel`是不是`null`,如不是`null`那就调用`cancel`函数,那么这是什么意思呢?我们`cancel`是在绑定`onclick`事件之前声明的一个全局变量,但是在声明的时候我们给了初始值为`null`,那么在我们点击发送请求按钮的时候,先进行判断`cancel`是不是为`null`,因为是第一次点击,所以`cancel`肯定是`null`,那么就会继续往下进行发送`ajax`请求。当发送请求的过程中,在`cancelToken`的回调中使得`cancel`重新赋值为一个函数对象。但当请求完成之后会在`then`的回调中重新将`cancel`赋值为`null`,至此一个完整的网络请求流程就完成了,在整个流程中`cancel`经历了`null` -> 函数 -> `null`
那么假设我们点击了一次按钮,发送了一次请求,然后又点击了一次按钮,如果在第二次点击按钮发送`ajax`请求前,判断发现`cancel`的值并不为`null`的话,那么是不是代表着上一次的请求还并没有完成?那么这个时候的`cancel`是不是就是用于取消上一次请求的函数?那么我们调用`cancel`函数是不是就取消了上一次请求?然后继续往下进行就开启了当前请求。那么我们来看一下效果:
![iShot2022-03-23 16.37.21](https://file.lynchow.com/2022-03-23-083748.gif)
现在我们便实现了恶意多次发送请求的话取消上一次请求只保留最新一次请求的需求。
当然关于取消请求的实现原理我们会在源码分析的过程中再详细介绍。这节课的内容我们先就到这。

0
note/lesson04_creation_process_of_axios.md

Loading…
Cancel
Save