后端小白程序员的axios学习笔记
文章目录
axios
写在前面
本博文仅作为个人学习过程的记录,可能存在诸多错误,希望各位看官不吝赐教,支持错误所在,帮助小白成长!
一、入门了解
1.1、什么是axios?
Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中。
简单来说,就是我们可以借助axios发送一些http请求,例如我们最熟悉的GET、POST请求,再例如我们学习过的Ajax异步请求。那么说到这里,你肯定会想起JQuery,因为我们目前在前端页面中都还是使用JQuery的库向服务器发送请求。
1.2、为什么学习axios?
随着前端框架Vue、React的出现,催生了axios这种轻量级的库,我们不必再使用庞大而复杂JQuery库来完成请求发送的操作。并且axios的功能更强大,并且支持Promise API。目前主流的前端框架(Vue、React)都极力推荐axios,并且对其做了积极的整合。
axios特性:
- 从浏览器中创建 XMLHttpRequests
- 从 node.js 创建 http 请求
- 支持 Promise API
- 拦截请求和响应
- 转换请求数据和响应数据
- 取消请求
- 自动转换 JSON 数据
- 客户端支持防御 XSRF
axios中文网|axios API 中文文档 | axios (axios-js.com)
1.3、前置内容与环境准备
在学习axios时,我们默认已经了解并学习了以下知识:
Promise
AJAX
1.3.1、json-server安装使用
既然是发送Http请求,那么我们就必须要有一个接收响应我们请求的服务端。这里推荐使用json-server
github仓库地址:typicode/json-server: Get a full fake REST API with zero coding in less than 30 seconds (seriously) (github.com)
0配置在30秒内创建一个虚假的REST API (REST是什么,学过后端的应该都懂,这里略过),直接开搞!
-
首先需要有node环境,然后创建项目文件夹后,在Terminal中打开文件夹,执行
npm init
进行项目初始化! -
下载json-server到项目本地lib(你也可以选择全局安装)
npm install --save-dev json-server
-
在项目根目录下创建
db.json
(这就相当于我们的数据库,请求的响应的数据都从这里面获取),可以按需修改,这是官网给的默认数据格式:{ "posts": [ { "id": 1, "title": "json-server", "author": "typicode" } ], "comments": [ { "id": 1, "body": "some comment", "postId": 1 } ], "profile": { "name": "typicode" } }
-
在
package.json
中加上服务启动脚本"scripts": { "test": "echo \"Error: no test specified\" && exit 1", + "server": "json-server --watch db.json" },
如果是全局安装,可以在db.json所在目录下直接使用
json-server --watch db.json
启动服务 -
访问
localhost:3000
-
发送请求,获取数据。例如
GET localhost:3000/posts/1
。现在你就可以将其作为一个虚拟的服务器进行请求访问了!(更多高级用法请参考官方文档!)
1.3.2、axios安装
npm安装使用
npm install axios --save
cdn引入
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
如果速度很慢,可以在BootCDN - Bootstrap 中文网开源项目免费 CDN 加速服务上获取axios的国内cdn
<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.21.1/axios.js"></script>
<!--或者-->
<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.21.1/axios.min.js"></script>
1.4、入门案例
这里我们结合使用vue,并使用axios完成从json-server获取数据:(引用了bootstrap组件库)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<link
href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha384-+0n0xVW2eSR5OomGNYDnhzAbDsOXxcvSN1TPprVMTNDbiYZCxYbOOl7+AMvyTG2x"
crossorigin="anonymous"
/>
</head>
<body>
<script
src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/js/bootstrap.bundle.min.js"
integrity="sha384-gtEjrD/SeCtmISkJkNUaaKMoLD0//ElJ19smozuHV6z3Iehds+3Ulb9Bn9Plx0x4"
crossorigin="anonymous"
></script>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.21.1/axios.js"></script>
<div class="container" id="app">
<div class="row">
<div>
<h1>Quick Start</h1>
<hr />
</div>
<div class="col-2">
<button type="button" class="btn btn-primary" @click="getAllPosts()">
获取所有post
</button>
</div>
<div class="col-2">
<button type="button" class="btn btn-primary" @click="getComments()">
获取comment/1
</button>
</div>
</div>
<hr />
<div class="row">
<div>
<textarea class="form-control" rows="8">{{response}}</textarea>
</div>
</div>
</div>
</body>
<script>
const app = new Vue({
el: '#app',
data: {
response: null,
},
methods: {
getAllPosts() {
axios.get('http://localhost:3000/posts').then((response) => {
this.response = response.data
})
},
getComments() {
axios.get('http://localhost:3000/comments/1').then((response) => {
this.response = response.data
})
},
},
})
</script>
</html>
这段代码的核心内容就是:
axios.get('http://localhost:3000/posts').then(...)
效果展示:
首先我们可以看出,使用axios进行请求发送和响应处理的手段有浓浓的Promise的味道。那我是不是可以理解为,我们的Http请求发送任务是一个异步任务??
除了使用axios.get(url)这种方式,我们还可以像JQuery中那样对请求进行配置:
axios(url[, config])
axios({
url: 'http://localhost:3000/posts',
method: 'get',
params: {
id: 1,
},
}).then(...)
这样进行详细配置后,发送的请求URL是:http://localhost:3000/comments?id=1
以上就是我们axios的一个简单入门使用案例,下面我们将从API、请求配置等多个方面进行学习。
二、axios API
2.1、请求方法
我们主要来看看axios有哪些常用的方法。前面我们使用过axios.get(url[,config])
和axios(url[,config])
随着RESTFul风格的流行,我们的请求方式不再拘泥于GET、POST,新的请求方式例如PUT、DELETE、PATCH得以大量使用,axios也为这些请求方式提供了方法别名:
axios.request(config)
axios.get(url[, config])
axios.delete(url[, config])
axios.head(url[, config])
axios.options(url[, config])
axios.post(url[, data[, config]])
axios.put(url[, data[, config]])
axios.patch(url[, data[, config]])
所以他们都是用于发请求的,并且请求的配置都没有差别,你可以根据需要选择对应请求方式的axios请求方法。我们这里就以post为例,向服务器发送一个添加数据的请求:
const app = new Vue({
el: '#app',
methods: {
addComment() {
axios
.post('http://localhost:3000/comments', {
id: 2,
body: '继续加油!!fighting!',
postId: 2,
})
.then(console.log)
},
},
})
其他方法除了在请求方式上存在区别外,其他的配置都一样,按需选用即可!
2.2、并发请求
axios.all()
,有点类似promise.all()
,多个请求发送,然后返回结果。
const app = new Vue({
el: '#app',
methods: {
getPostsAndComments() {
// 请求1:获取所有的post
let posts = function () {
return axios
.get('http://localhost:3000/posts')
.then((response) => response.data)
}
// 请求2: 获取所有的comment
let comments = function () {
return axios
.get('http://localhost:3000/comments')
.then((response) => response.data)
}
axios
.all([posts(), comments()])
.then(console.log)
.catch((reason) => console.log(reason))
},
},
})
只要all
的参数列表中,一个请求失败那么就不会得到最终结果,并且失败异常将会被捕获输出。
全部成功后,所有请求的结果,将按请求参数的顺序放置在一个数组中。
效果演示:

2.3、创建axios实例
axios.create([config])
前面我们都是直接使用axios
的方法进行请求发送。当我们需要给多台服务器发送请求,并且请求的配置极其复杂时,我们可以针对每台服务器创建一个axios对象,将进行配置,然后以后通过实例方法发送请求即可。
let instance = axios.create({
baseURL: 'https://localhost:3000/api/',
timeout: 1000,
// ... 其他配置
})
let instance2 = axios.create({
baseURL: 'http://localhost:8080/movie/222'
// ... 其他配置
})
axios实例的方法,和我们直接使用axios
调用的方法是相同的!
三、请求配置
这章我们需要了解axios请求的一些配置。作为一个以发送请求为主要功能的库,对请求进行配置是特别重要的!
中文官网上有对配置的详细说明:axios | 请求配置
我们选择几个较为常用的配置进行说明:
-
url
:被请求的服务器URL -
method
:请求方式,默认为get -
baseURL
:作为请求的URL的基础部分,将自动拼接在url前面。(常用于axios实例中配置)可以避免重复编写URL。 -
transformRequest/Response
- transformRequest: 用于在发送请求之前,对请求数据进行修改。(只能用于POST、PUT、PATCH请求)
- tranformResponse: 用于在接收到响应前(传给then之前),对响应的数据做转换
-
header
:请求头,可以通过kv形式进行修改。 -
params
:请求时发送的URL参数。(与后面的data区分) -
paramsSerializer
:params的序列化函数,它决定了params如何拼接到URL上去。(一般不做修改) -
data
:请求的主体数据,并不会被拼接到URL上,但是能被服务器解析。只适用于PUT、PATCH、POST请求 -
timeout
:设置请求超时时间,超过此限制后请求将被中断,0为无超时时间。 -
cancelToken
:用于取消请求,后续我们会详细说明如何取消请求。(常用于请求按钮防抖)
四、响应结构说明
我们通过get等请求获取到的响应可以通过then
向下传递并进行处理。我们需要了解响应数据的结构,以便于我们更好地利用响应数据!
-
data
: 这是我们响应数据的主体,是服务器响应我们请求后返回的数据 -
status
: 服务器响应的HTTP状态码 -
statusText
: 服务器响应的HTTP状态信息 -
headers
: 响应头 -
config
: 服务器响应的请求 的对应配置信息 -
request
: 响应的具体请求对象
五、默认配置
前面我说使用axios实例来单独管理我们的配置,具体如何管理,这里我们就会详细说明
5.1、配置全局默认配置
使用axios.default.xxx = xxxx
,就可以将请求配置中的xxx全局性配置为xxxx。此时如果你创建的axios实例中没有对其配置项进行配置,将使用这个全局默认配置!如果配置了,那就涉及到配置优先级的问题,后面再说!
axios.defaults.baseURL = 'http://localhost:3000'
axios.defaults.timeout = 2000
let posts = axios.create()
posts.get('/posts').then(console.log)

通过输出的response对象来看,创建的axios对象确实使用了我们全局配置的baseURL和timeout!
5.2、实例的默认配置
为实例配置默认配置有两种方式:
- 在使用
axios.create
创建时作为参数传入 - 使用
instance.defaults.xxx
进行设置
let posts = axios.create({
timeout: 3000,
})
posts.defaults.timeout = 4000
对于同一个配置项,当两种配置方式同时出现时,后者将会覆盖前者!
5.3、配置的优先级
在进行请求配置时,会按照一下顺序进行搜索:
- lib/default.js中axios库的默认值
- 实例的默认值
- 请求方法中传入的config参数
后面的都会将前面的配置覆盖掉!所以说优先级最高的就是请求方法中的config参数!
其次是实例的默认配置、最后是axios全局的默认配置(即axios库的默认配置)
axios.defaults.baseURL = 'http://localhost:3000'
axios.defaults.timeout = 2000
let posts = axios.create({
timeout: 3000,
})
posts.defaults.timeout = 4000
posts.get('/posts', { timeout: 5000 }).then(console.log)
这样配置后,最终响应结果中的config,timeout值为5000。

感兴趣的可以尝试层层删除timeout的配置,来验证一下配置优先级!
六、拦截器
个人有幸之前学习过Flume,与Flume拦截器功能一样,它们的功能都是将数据先进行拦截,然后进行处理后再决定是否放行。并不是简单的拦截打回。
axios的拦截器分为请求拦截器和响应拦截器。它们所做的工作分别是:将请求/响应数据,拦截进行必要的处理后发送/返回。
添加拦截器代码示例:
// 添加请求拦截器
axios.interceptors.request.use(function (config) {
// 在发送请求之前做些什么
return config;
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error);
});
// 添加响应拦截器
axios.interceptors.response.use(function (response) {
// 对响应数据做点什么
return response;
}, function (error) {
// 对响应错误做点什么
return Promise.reject(error);
});
这个结构是不是感觉似曾相识,的确,它和我们之前在Promise中使用的then
方法极其类似!它会先根据请求/响应状态执行对应的回调函数。如果请求拦截器,出错返回了rejected Promise对象,那么响应拦截器就会执行失败回调!
拦截器的调用顺序,最先声明的请求拦截器会最后调用,而响应拦截器则是按声明顺序调用!要搞清楚为什么需要阅读axios实现源码。
七、错误处理
既然采用PromiseAPI,那么进行错误处理只需要在调用链后加上.catch()
即可完成对错误的捕捉和处理!
axios.get('http://localhost:3000/posts')
.then(console.log)
.catch(function (error) {
// ... 异常处理
});
在请求配置中指定错误处理的范围:请求配置中有一项名为validateStatus
的配置项,是用于验证请求状态的函数。这是默认实现:
validateStatus: function (status) {
return status >= 200 && status < 300; // default
}
当状态码 200 < status < 300 则视为请求状态正常,否则视为请求异常!
你可以对异常请求的判断范围做修改:
validateStatus: function (status) {
return status < 500
}
八、取消请求
为什么会有取消请求的需求?试想一个按钮点击一下发送一个请求,如果用户反复点击,就会一下发送很多请求,这样会给客户端造成压力,如果我们能够在用户第二次点击请求按钮时,将先前还未响应的请求取消,那么多次点击也就只有一次响应!这样就实现了按钮防抖!
而要实现请求取消,需要一个请求配置:cancelToken
。
需要使用json-server的启动选项
-d
来指定服务器响应时间,否则来不及取消请求!(-d 3000为响应时间 3s)
方式一:使用 CancelToken.source
工厂方法创建 cancel token
const CancelToken = axios.CancelToken
const source = CancelToken.source()
axios.defaults.baseURL = 'http://localhost:3000'
const app = new Vue({
el: '#app',
methods: {
sendRequest() {
axios
.request('/posts', {
// 将source.token设置为cancelToken
cancelToken: source.token,
})
.then(console.log)
.catch(console.log)
},
cancelRequest() {
source.cancel('Request was canceled')
},
},
})
但是这种方式好像只能取消一次!?
方式二:传递一个 executor 函数到 CancelToken
的构造函数来创建 cancel token
const CancelToken = axios.CancelToken
// 变量,用于接收cancel函数
let cancel = null
axios.defaults.baseURL = 'http://localhost:3000'
const app = new Vue({
el: '#app',
methods: {
sendRequest() {
axios
.request('/posts', {
// 传递一个 executor 函数到 CancelToken 的构造函数来创建 cancel token
cancelToken: new CancelToken(function (c) {
// 含cancel函数参数赋值给变量
cancel = c
}),
})
.then((response) => {
console.log(response.data)
// 已完成响应销毁cancel函数
cancel = null
})
.catch(console.log)
},
cancelRequest() {
if (cancel !== null) {
// 执行cancel函数,清除token
cancel()
}
},
},
})
当前效果还是使用两个按钮来实现,我们将俩个按钮的代码中和到一起,即可实现按钮防抖:
sendRequest() {
// 先判断cancel函数是否有效
if (cancel !== null) {
// 调用取消请求
cancel()
}
axios
.request('/posts', {
// 传递一个 executor 函数到 CancelToken 的构造函数来创建 cancel token
cancelToken: new CancelToken(function (c) {
// 含cancel函数参数赋值给变量
cancel = c
}),
})
.then((response) => {
console.log(response.data)
// 已完成响应,销毁cancel函数
cancel = null
})
.catch(console.log)
},
使用这种方式,我们每个请求都会new一个cancelToken,以及对应的cancel函数,所以每次点击都不相影响。
而方式一使用的cancelToken都是同一个,一旦cancel后,就无法变更了。所以只能取消一次。
8.1、请求取消原理
我们使用第二种方式创建cancelToken时,调用了new CancelToken()
,并且我们传入了一个函数,函数中我们将函数的参数c,赋值给了一个变量。而这个参数c,则是我们所说的cancel函数。
请求取消其原理还是使用了Promise,当我们调用对应的函数时,会改变Promise对象的State,从而触发对应的请求取消操作,最核心的取消操作就是request.abort()
(这是ajax取消请求的方法!)
这里是CancelToken的源码:/lib/cancel/CancelToken.js

var resolvePromise;
this.promise = new Promise(function promiseExecutor(resolve) {
resolvePromise = resolve;
});
这段代码执行后,this.promise的命运被交给了resolvePromise
,一旦resolvePromise()被执行,this.promise的State就会变为fulfilled
!
然后看下面的executor
,这就是我们new CancelToken时传入的函数function(c) { ... }
executor调用时又传入了一个function cancel(message)
作为参数,也就是我们的参数c,此时我们的c就可以当做函数调用了!而刚好在我们自己的代码中,我们将c又赋值给了一个变量x
。
那我们再来看看这个cancel函数:
function cancel(message) {
if (token.reason) {
// Cancellation has already been requested
return;
}
token.reason = new Cancel(message);
resolvePromise(token.reason);
}
首先判断是否已经被要求取消,若是直接返回,否则先设置取消的reason,表示已经进行过请求取消了,然后调用resolvePromise()
,并将reason传入,这相当于在Promise中调用了resolve()。那么也就是说在我们的代码中通过这个x()
就可以改变CancelToken对象内部属性promise的状态!执行后this.promise的State将被设置为fulfilled
!但是到这还没完,还没有调用request.abort()
正式取消请求!
再来到./lib/adapters/xhr.js
,看到这段代码:

首先会在config中查看我们是否设置了cancelToken,如果设置了,为我们设置的cancelToken的内部的promise对象加入一个订阅者(或者处理程序),但是注意这里then()
只设置了resolve,即针对fulfilled状态的回调:
function onCanceled(cancel) {
if (!request) {
return;
}
request.abort();
reject(cancel);
// Clean up request
request = null;
}
这就串上了我们CancelToken的代码,这里的config.cancelToken.promise
和CancelToken中的this.promise
就是同一个东西!当我们调用cancel()方法后,resolvePromise会将promise的State置为fulfilled,然后这里的处理程序接收到promise转态变化,调用request.abort()
取消了请求,并将请求对象清除!
这种“控制权”的来回传递,对用户封装了大部分代码。(代码有点绕,需要反复理解梳理!)
方式一源码:
方式一中,我们使用source.token
作为我们请求的cancelToken。通过看其源码,可以发现和方式二没有太大区别,只是将new CancelToken()
的过程进行了封装!
但是很显然这是一个一次性的token,使用完毕后,就必须使用source()
创建新的!而使用方式二,每个请求都会new一个CancelToken!
九、vue-axios
在vue中使用axios:
-
npm 安装axios、axios-vue
npm install --save axios vue-axios
-
引入依赖,并使用
import Vue from 'vue' import axios from 'axios' import VueAxios from 'vue-axios' Vue.use(VueAxios, axios)
-
它会像vue-router一样,向每个组件注入属性。
再组件内的script代码中,使用
this.$http
,this.axios
,axios
就可以作为axios实例进行使用,使用请求方法发送请求了!!(直接使用axios
时,要通过import在script代码中导入)methods: { sendRequest() { axios.defaults.baseURL = 'http://localhost:3000' axios.get('/posts') .then((response) => { console.log(response.data) }) } }
但是他们三者有什么联系,axios的请求配置范围目前我们还不清楚。
目前只知道,当我们在main.js中使用axios.default.xxx=x
进行全局默认配置后,在其他组件中无论是使用this.axios
还是this.$http
,都会生效!
你也可以使用axios.create
创建axios实例进行配置后并export,然后在其他组件中impor进行使用!
现在我坚信他们仨就是同一个东西:(vue-axios/src/index.js)

(不知道我看的位置对不对~~*)
ipt代码中,使用this.$http
,this.axios
,axios
就可以作为axios实例进行使用,使用请求方法发送请求了!!(直接使用axios
时,要通过import在script代码中导入)
methods: {
sendRequest() {
axios.defaults.baseURL = 'http://localhost:3000'
axios.get('/posts')
.then((response) => {
console.log(response.data)
})
}
}
但是他们三者有什么联系,axios的请求配置范围目前我们还不清楚。
目前只知道,当我们在main.js中使用axios.default.xxx=x
进行全局默认配置后,在其他组件中无论是使用this.axios
还是this.$http
,都会生效!
你也可以使用axios.create
创建axios实例进行配置后并export,然后在其他组件中impor进行使用!
现在我坚信他们仨就是同一个东西:(vue-axios/src/index.js)

(不知道我看的位置对不对~~*)
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步