一、前言
1.特点
- 在浏览器端借助axios向服务端发送ajax请求。
- 在nodejs中借助axios向服务端发送http请求
- 支持Promise
2.使用
在项目中一般使用包管理工具npm、yarn来安装相关依赖。而在一般的练习环节就使用script标签引入就行。
如果响应过慢的话,使用BootCDN 来使用资源
- 关于请求的链接
用mock或者json-server模拟一下接口数据,能方便的去更好的练习
- 关于调试
在 gitee上这个文件中node_modules/axios/dist/mine-axios.js,是进行打包处理了的,直接通过script引入来进行相关的调试
详细源码分析,关键地方都做了注释的,可以查看node_modules/axios/lib文件夹
二、基本使用
可以使用axios基本方法来发送请求,也可以使用它的方法
1.axios()
- axios({url,config})函数接收一个对象 ,包括请求地址和相关参数。
- 返回一个promise对象
axios({
method:'GET',
url:'http://localhost:3000/posts/3',
//设置请求体
data:{
}
})
.then(res=>{
console.log(res)
})
复制代码
更多的使用方法在axios官网去看相关文档
2.配置对象解析
- url
指明请求的地址,必需的
- method
指明请求的方法,如果没有指定method,那么请求默认使用get方法
- baseURL
设置请求链接的基础结构,它会和url进行一个结合
- data
作为请求主体被发送的数据,适用在post、put、patch这几个请求方法中
- timeout
指定请求超时的毫秒数,如果超过指定时间,请求就会被中断
- transformRequest
允许在向服务器发送前,修改请求数据。只能用在post、put、patch这几个请求方法中,
transformRequest: [function (data) {
// 对 data 进行任意转换处理
return ;
}],
复制代码
**注意:**数组中的函数必须返回一个字符串/ArrayBuffer/Stream
- transformResponse
在响应数据传递给then/catch之前,允许修改响应数据
- headers
自定义请求头,是一个对象
- params
和请求一起发送的URL参数,必须是一个无格式对象
params:{
ID:12345
}
复制代码
3.默认配置
为了简便每次的请求操作,可以设置axios的一个默认配置
axios.defaults加上相关默认参数设置默认配置
axios.defaults.method = 'GET'
复制代码
4.创建实例对象发送请求
利用axios.create()创建实例对象,从而发送请求
const obj = axios.create({
baseURL:'',
methods:'GET'
})
obj({
url:''
})
.then(response=>{
})
复制代码
5.axios拦截器
- 基本使用
拦截器注主要是函数,分为两大类(请求、响应拦截器)。
请求拦截器:它的主要任务就是在请求被发送之前通过回调函数进行一些操作
响应拦截器: 通过回调函数在数据结果被then、catch接收处理之前,先对数据进行处理
拦截器中主要两个回调函数(成功和失败的回调)—-与promise息息相关.。如果拦截器调用的失败的回调,就像下面例子这样,就返回rejected状态的promise
请求拦截器中:成功的回调函数参数config: 表明我们可以对请求的参数配置对象进行更改。
响应拦截器中:成功的回调函数参数response:它是axios请求默认的返回结果
// 1. 添加请求拦截器
axios.interceptors.request.use(function (config) {
// 在发送请求之前做些什么
return
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error);
});
// 2. 添加响应拦截器
axios.interceptors.response.use(function (response) {
// 对响应数据做点什么
return
}, function (error) {
// 对响应错误做点什么
return Promise.reject(error);
});
复制代码
- 数据处理顺序
在设置有拦截器的axios请求中,数据首先是先经过请求拦截器——>响应拦截器——>then/catch
- 多个拦截器执行顺序问题
多个请求拦截器:后定义的先执行
多个响应拦截器:先定义的先执行
具体原因查看axios源码分析
6.取消请求
//2.声明全局变量
let cancel = null;
axios({
method:'GET',
url:'',
//1.axios配置中添加对象的属性
cancelToken:new axios.CancelToken(function(c){
//3.将c的值赋给cancel
cancel = c
})
})
//4.执行cancel函数
cancel()
复制代码
三、axios请求响应结构

- config
包含请求的相关配置信息

- data
包含服务器响应的结果
- headers
响应的头信息
- request
原生的ajax请求对象——XMLHttpRequest对象
- status
服务器响应的http状态码
- statusText
服务器响应的http状态信息
四、axios源码分析
目录文件分析

详细的源码分析、文件功能在源码中都有注释
axios创建过程
在源码axios.js中可以看到axios的创建,接下来进行一一分析:
var axios = createInstance(defaulyts)
复制代码
axios.js中createInstance(defaults)函数
function createInstance(defaultConfig) {
//1.Axios.js
var context = new Axios(defaultConfig);
//2.bind
var instance = bind(Axios.prototype.request, context);
//3.复制
utils.extend(instance, Axios.prototype, context);
utils.extend(instance, context);
return instance;
}
复制代码
1. Axios.js
var context = new Axios(defaultConfig);
复制代码
这一句代码需要追溯到Axios.js中
function Axios(instanceConfig) {
this.defaults = instanceConfig;
this.interceptors = {
request: new InterceptorManager(),
response: new InterceptorManager()
};
}
Axios.prototype.request = function request(config) {
//...
};
Axios.prototype.getUri = function getUri(config) {
//...
};
...
//往原型上添加更多的方法
复制代码
在以上代码中主要注意以下几点
- 设置defaults,配置默认对象
- 设置interceptors添加请求/响应拦截器
刚好对应之前拦截中的设置方案
axios.interceptors.request.use()
axios.interceptors.response.use()
复制代码
- 在Axios原型对象上添加方法
在axios实例化对象上就可以调用我们之前使用的axios.request等等许多方法
这也就是为什么我们在使用axios时,可以直接使用它,也可以通过调用相关的方法来使用
**小结:**经过这一步 实例化的对象能够通过调用方法来发送请求等等,但是不能像平常使用的那样直接调用。因此需要进行接下来的步骤
2.bind
var instance = bind(Axios.prototype.request, context);
复制代码
bind是一个绑定函数,在源码中进行的一个封装,将this指向context
这里的instance 与 Axios.prototype.request(发送请求) 代码一致,它是一个函数
3.复制
utils.extend(instance, Axios.prototype, context);
utils.extend(instance, context);
复制代码
之前的instance只是一个函数而已。
这里的extend方法是将Axios.prototype和实例化对象context的方法都添加到instance身上。
经过这一步,
- instance 是一个函数
- 作为对象,具有很多方法
4.模拟实现
在以上过程中简单描述了创建的过程,接下来的话,模拟实现的创建过程会更加清晰一点
//构造函数
function Axios(config) {
//初始化
this.defaults = config;
this.interceptors = {
request:{},
response:{}
}
}
//原型添加相关方法
Axios.prototype.request = function (config) {
console.log(config)
}
Axios.prototype.get = function (config) {
//调用request方法使得可以请求
return this.request(config);
}
Axios.prototype.post = function (config) {
//调用request方法使得可以请求
return this.request(config);
}
//声明一个函数
function createInstance(config) {
//1.实例化一个对象
let context = new Axios(config);//不能当作函数使用
//2.创建请求函数
let instance = Axios.prototype.request.bind(context);//可以直接调用request方法
//3.复制方法(让instance成为一个对象)
Object.keys(Axios.prototype).forEach(key=>{
instance[key] = Axios.prototype[key].bind(context);
//注意这里需要绑定到context上,this指向始终指向context
})
//4.为instance添加默认配置(defaults和interceptors)
Object.keys(context).forEach(key=>{
instance[key] = context[key];
})
return instance;
}
var axios = createInstance({method:'get'})
axios({method:'post'})
axios.get({data:'beatrix'})
复制代码
axios请求过程
从上面的axios创建过程分析,相信都清楚了axios的调用实质上就是Axios.prototype.request的调用,包括其他原型对象上的方法都是基于request方法的,因此接下来具体分析axios的请求过程
axios({
}).then(res=>{
})
复制代码
1.request
Axios.prototype.request = function request(config) {
//1. 检查传递的参数
if (typeof config === 'string') {
config = arguments[1] || {};
config.url = arguments[0];
} else {
config = config || {};
}
//2. 将默认配置和用户调用时传入的配置进行合并
config = mergeConfig(this.defaults, config);
//3. 对请求方法做判断
if (config.method) {
config.method = config.method.toLowerCase();
} else if (this.defaults.method) {
config.method = this.defaults.method.toLowerCase();
} else {
config.method = 'get';
}
//4.创建拦截器中间件,undefined做补位
var chain = [dispatchRequest, undefined];
//5. 创建一个成功的promise
var promise = Promise.resolve(config);
//6.遍历请求拦截器和响应拦截器
this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
chain.unshift(interceptor.fulfilled, interceptor.rejected);
});
this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
chain.push(interceptor.fulfilled, interceptor.rejected);
});
//7.依次取出chain的回调函数
//首先是dispatch中
while (chain.length) {
promise = promise.then(chain.shift(), chain.shift());
}
//8.返回一个promise对象
return promise;
};
复制代码
在第4步中,dispatchRequst是一个函数,用来发送请求的,通过它来调用HTTP、XMLHttprequest。
在第7步中,它将作为promise的成功回调函数被执行
2. dispatchRequest.js
var chain = [dispatchRequest, undefined];
复制代码
在dispatchRequest中,主要做了以下几件事
function dispatchRequest(config) {
//1.确保头信息存在
config.headers = config.headers || {};
//2.对请求数据进行初始化转化
config.data = transformData(
config.data,
config.headers,
config.transformRequest
);
// 3.合并一切其他头信息的配置项
config.headers = utils.merge(
config.headers.common || {},
config.headers[config.method] || {},
config.headers
);
//4.将配置项中关于方法的配置项全部移除
utils.forEach(
['delete', 'get', 'head', 'post', 'put', 'patch', 'common'],
function cleanHeaderConfig(method) {
delete config.headers[method];
}
);
//5. 获取适配器对象 http xhr
var adapter = config.adapter || defaults.adapter;
//6. 发送请求, 返回请求后 promise 对象 ajax HTTP
return adapter(config).then(function onAdapterResolution(response) {
throwIfCancellationRequested(config);
// Transform response data
response.data = transformData(
response.data,
response.headers,
config.transformResponse
);
//设置 promise 成功的值为 响应结果
return response;
}, function onAdapterRejection(reason) {
if (!isCancel(reason)) {
throwIfCancellationRequested(config);
// Transform response data
if (reason && reason.response) {
reason.response.data = transformData(
reason.response.data,
reason.response.headers,
config.transformResponse
);
}
}
//设置 promise 为失败, 失败的值为错误信息
return Promise.reject(reason);
});
};
复制代码
**注意:**dispatchRequest函数的返回结果是根据promise.then的返回结果决定的(也就是说它的返回结果同样是一个promise对象)
3.xhr.js
在上述第六步中,发送请求的操作中
return adapter(config).then(function onAdapterResolution(response) {
}, function onAdapterRejection(reason) {
});
复制代码
在以上adpter函数中是调用xhr.js中的xhrAdapter函数,作用就是发送ajax请求,返回一个promise对象
4.小结
axios的请求过程实际上就是:::调用Axios.prototype.request函数——>调用dispatchRequest函数——>调用xhrAdapter函数,一步步的返回结果,最终request函数执行完毕,axios也执行完毕,返回一个promise对象
如果看到这里还不是很懂的话,就建议去看源码分析,里面有详细的解释,因为axios请求过程涉及到promise的大量运用,建议把promise多看几遍
5.模拟实现
模拟实现的话,是对上面的功能进行简写,只有核心的结构,如果上面还不是很懂的话,看这个应该可以了。
//构造函数
function Axios(config) {
//初始化
this.defaults = config;
this.interceptors = {
request:{},
response:{}
}
}
//1. 原型添加request
Axios.prototype.request = function (config) {
//创建一个promise对象
let promise = Promise.resolve(config);
//声明一个数组
let chains = [dispatchRequest,undefined];//undefined占位
//调用then方法指定回调
let result = promise.then(chains[0],chains[1]);//执行第一个回调
//返回promise结果
return result;
}
//2.dispatchRequest函数
function dispatchRequest(config) {
//调用适配器发送请求
return xhrAdapter(config).then(response=>{
return response
},error=>{
throw error
})
}
//3.adapter适配器
function xhrAdapter(config) {
console.log('xhrAdapter 函数执行');
return new Promise((resolve,reject)=>{
//发送ajax请求
let xhr = new XMLHttpRequest();
//初始化
xhr.open(config.method,config.url);
//发送
xhr.send();
//绑定事件
xhr.onreadystatechange=function(){
if(xhr.readyState==4){
if(xhr.status==200){
//成功状态
resolve({
//最终返回请求结果
config:config,
data:xhr.response,
//....
});
}else{
reject('请求失败')
}
}
}
})
}
//4.创建axios
let axios = Axios.prototype.request.bind(null)
//5.使用
axios({
method:'GET',
url:'http:/localhost:3000'
}).then(response=>{
console.log(response)
复制代码
axios拦截器
首先看看拦截器的执行:
axios.interceptors.request.use(function (config) {
// 在发送请求之前做些什么
return
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error);
});
复制代码
- interceptors属性
还记得在axios创建过程中,Axios构造函数的初始化了这个属性,其中包含了(request、response)两个属性,
function Axios(instanceConfig) {
this.interceptors = {
request: new InterceptorManager(),
response: new InterceptorManager()
};
}
复制代码
1.InterceptorManager
以下就是InterceptorManager构造函数
function InterceptorManager() {
//创建一个属性
this.handlers = [];
}
InterceptorManager.prototype.use = function use(fulfilled, rejected) {
this.handlers.push({
fulfilled: fulfilled,
rejected: rejected
});
return this.handlers.length - 1;
};
复制代码
经过use方法的使用,上述的handlers就会有值(是个对象),就是我们在使用use方法传入的回调函数
2.执行顺序问题
之前多个拦截器的执行顺序问题还没有解决,现在放到这里来解决
//第一步
axios.interceptors.request.use(function one(res) {
console.log("请求拦截器成功1")
},function one(err) {
console.log("请求拦截器失败1")
})
//第二步
axios.interceptors.request.use(function two(res) {
console.log("请求拦截器成功2")
},function two(err) {
console.log("请求拦截器失败2")
})
//第三步
axios.interceptors.response.use(function one(res) {
console.log("响应拦截器成功1")
},function one(err) {
console.log("响应拦截器失败1")
})
//第四步
axios.interceptors.response.use(function two(res) {
console.log("响应拦截器成功2")
},function two(err) {
console.log("响应拦截器失败2")
})
axios({
method:'GET',
url:'http://localhost:3000'
}).then(res=>{
console.log(res)
})
复制代码
我们来看执行顺序问题:
- 第一步执行完成之后,request对象上有属性handlers:一个数组对象包含两个回调函数one

- 第二、三、四步执行完成之后,request和response身上都有了对应的handlers【{one},{two}】

- axios开始发送请求,进入请求过程中request函数中,遍历请求拦截器
//4.创建拦截器中间件,undefined做补位
var chain = [dispatchRequest, undefined];
//5. 创建一个成功的promise
var promise = Promise.resolve(config);
//6.遍历请求拦截器和响应拦截器
this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
chain.unshift(interceptor.fulfilled, interceptor.rejected);
});
复制代码
经过unshift操作之后,handlers对象数组中的每个对象包含的两个回调函数(成功/失败)都被压入chain中,chain数组变成如下这样:

- 通过push方法,来添加响应拦截器中的方法,最后chain数组的结果
//遍历响应拦截器
this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
chain.push(interceptor.fulfilled, interceptor.rejected);
});
复制代码

- 接下来就使用shift方法对这些回调函数取出并且执行
shift是会改变原数组的,因此依次取出来的就是成功和失败的回调函数,最终遍历完整个chain数组,在Axios.rototype.request方法中返回一个promise结果,剩下的就交给axios().then来对结果做处理
while (chain.length) {
//依次取出 chain 的回调函数, 并执行
promise = promise.then(chain.shift(), chain.shift());
}
return promise;
复制代码
3. 模拟实现
//1.Axios.prototype.request方法
function Axios(config) {
//初始化
this.defaults = config
this.interceptors = {
request: new InterceptorManager(),
response: new InterceptorManager(),
}
}
Axios.prototype.request = function (config) {
//创建一个promise对象
let promise = Promise.resolve(config);
//创建chains数组
const chains = [dispatchRequest,undefined];
//处理拦截器
//请求拦截器放在chains前面
this.interceptors.request.handlers.forEach(item=>{
chains.unshift(item.fulfilled,item.rejected);
})
//响应拦截器放在chains后面
this.interceptors.response.handlers.forEach(item=>{
chains.push(item.fulfilled,item.rejected);
})
//遍历
while(chains.length>0){
promise = promise.then(chains.shift(),chains.shift())
}
return promise
}
//2.构建发送请求的函数dispatchRequest
function dispatchRequest() {
return new Promise((resolve,reject)=>{
resolve({
status:200,
})
})
}
//.3.构建axios
//创建实例
let context = new Axios({})
//创建axios函数
let axios = Axios.prototype.request.bind(context);
//将context属性添加到axios上
Object.keys(context).forEach(key=>{
axios[key] = context[key]
})
//4.拦截器管理器构造函数
function InterceptorManager() {
this.handlers = []
}
InterceptorManager.prototype.use = function (fulfilled, rejected) {
this.handlers.push({
fulfilled,
rejected,
})
}
//.5.设置请求/响应拦截器
axios.interceptors.request.use(function one(res) {
console.log("请求拦截器成功1")
},function one(err) {
console.log("请求拦截器失败1")
})
axios.interceptors.request.use(function two(res) {
console.log("请求拦截器成功2")
},function two(err) {
console.log("请求拦截器失败2")
})
axios.interceptors.response.use(function one(res) {
console.log("响应拦截器成功1")
},function one(err) {
console.log("响应拦截器失败1")
})
axios.interceptors.response.use(function two(res) {
console.log("响应拦截器成功2")
},function two(err) {
console.log("响应拦截器失败2")
})
//6.使用
axios({method:'GET'})
复制代码























![[桜井宁宁]COS和泉纱雾超可爱写真福利集-一一网](https://www.proyy.com/skycj/data/images/2020-12-13/4d3cf227a85d7e79f5d6b4efb6bde3e8.jpg)

![[桜井宁宁] 爆乳奶牛少女cos写真-一一网](https://www.proyy.com/skycj/data/images/2020-12-13/d40483e126fcf567894e89c65eaca655.jpg)