文章目录
主要目的是对 博客中axios的基本使用进行分析,提供依据。
0. 源码目录结构
├── /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 # 入口文件
1. axios为什么能有多种发请求的方法?
axios函数对应的是Axios.prototype.request方法通过bind(Axiox的实例)产生的函数
axios有Axios原型上的所有发特定类型请求的方法: get()/post()/put()/delete()
axios有Axios的实例上的所有属性: defaults/interceptors
后面又添加了create()/CancelToken()/all()
/**axios.js
* Create an instance of Axios
*
* @param {Object} defaultConfig The default config for the instance
* @return {Axios} A new instance of Axios
*/
function createInstance(defaultConfig) {
/*
创建Axios的实例
原型对象上有一些用来发请求的方法: get()/post()/put()/delete()/request()
自身上有2个重要属性: defaults/interceptors
*/
var context = new Axios(defaultConfig);
// axios和axios.create()对应的就是request函数
// Axios.prototype.request.bind(context)
var instance = bind(Axios.prototype.request, context); // axios
//bind返回一个原函数(Axios.prototype.request)的拷贝,并拥有指定的 this 值(Axios对象context)和初始参数(可无)。
// 将Axios原型对象上的方法拷贝到instance上: request()/get()/post()/put()/delete()
utils.extend(instance, Axios.prototype, context);
// 将Axios实例对象上的属性拷贝到instance上: defaults和interceptors属性
utils.extend(instance, context);
return instance;
}
// Create the default instance to be exported
var axios = createInstance(defaults);
// Expose Cancel & CancelToken
axios.Cancel = require('./cancel/Cancel');
axios.CancelToken = require('./cancel/CancelToken');
axios.isCancel = require('./cancel/isCancel');
// Expose all/spread
axios.all = function all(promises) {
return Promise.all(promises);
};
/**utils.js
* Extends object a by mutably adding to it the properties of object b.
*
* @param {Object} a The object to be extended
* @param {Object} b The object to copy properties from
* @param {Object} thisArg The object to bind function to
* @return {Object} The resulting value of object a
*/
function extend(a, b, thisArg) {
forEach(b, function assignValue(val, key) {
if (thisArg && typeof val === 'function') {
a[key] = bind(val, thisArg);
} else {
a[key] = val;
}
});
return a;
}
/* bind.js
绑定函数的this
*/
module.exports = function bind(fn, thisArg) {
return function wrap() {
var args = new Array(arguments.length); //arguments为函数内部可用的arguments对象
//arguments对象是所有(非箭头)函数中都可用的局部变量。你可以使用arguments对象在函数中引用函数的参数。此对象包含传递给函数的每个参数,第一个参数在索引0处。
for (var i = 0; i < args.length; i++) {
args[i] = arguments[i];
}
return fn.apply(thisArg, args);
//返回调用有指定this值(thisArg)和参数(args)的函数(fn)的结果
};
};
2. axios.create()返回的对象与axios的区别?
相同:
都是一个能发任意请求的函数: request(config)
都有发特定请求的各种方法: get()/post()/put()/delete()
都有默认配置和拦截器的属性: defaults/interceptors
不同:
默认匹配的值很可能不一样
instance没有axios后面添加的一引起方法: create()/CancelToken()/all()
// Factory for creating new instances
axios.create = function create(instanceConfig) {
return createInstance(mergeConfig(axios.defaults, instanceConfig));
};
3. axios运行的整体流程
4. Axios.prototype.request()都做了什么?
/**
* 用于发请求的函数
* 我们使用的axios就是此函数bind()返回的函数
* Dispatch a request
*
* @param {Object} config The config specific for this request (merged with this.defaults)
*/
Axios.prototype.request = function request(config) {
/*eslint no-param-reassign:0*/
// Allow for axios('example/url'[, config]) a la fetch API
if (typeof config === 'string') {
config = arguments[1] || {
};
config.url = arguments[0];
} else {
config = config || {
};
}
// 合并配置
config = mergeConfig(this.defaults, config);
// 添加method配置, 默认为get
config.method = config.method ? config.method.toLowerCase() : 'get';
/*
创建用于保存请求/响应拦截函数的数组
数组的中间放发送请求的函数
数组的左边放请求拦截器函数(成功/失败)
数组的右边放响应拦截器函数
*/
var chain = [dispatchRequest, undefined];
var promise = Promise.resolve(config);
// 后添加的请求拦截器保存在数组的前面
this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
chain.unshift(interceptor.fulfilled, interceptor.rejected); //fufilled即resolved
});
// 后添加的响应拦截器保存在数组的后面
this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
chain.push(interceptor.fulfilled, interceptor.rejected);
});
// 通过promise的then()串连起所有的请求拦截器/请求方法/响应拦截器
while (chain.length) {
promise = promise.then(chain.shift(), chain.shift());
}
// 返回用来指定我们的onResolved和onRejected的promise
return promise;
};
PS:请求拦截器在头部插入,响应拦截器在尾部插入。初始链为var chain = [dispatchRequest, undefined];是因为添加取出都是成对的
5. dispatchrequest()都做了什么?
请求响应数据体格式转换
/* dispatchRequest.js
对config中的data进行必要的转换处理
设置相应的Content-Type请求头
*/
config.data = transformData(
config.data,
config.headers,
config.transformRequest //执行请求体数据转换函数
);
...
var adapter = config.adapter || defaults.adapter;
return adapter(config).then(function onAdapterResolution(response) {
throwIfCancellationRequested(config);
/*
对response中还没有解析的data数据进行解析
json字符串解析为js对象/数组
*/
response.data = transformData(
response.data,
response.headers,
config.transformResponse
);
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
);
}
}
return Promise.reject(reason);
});
//default.js
function getDefaultAdapter() {
var adapter;
// Only Node.JS has a process variable that is of [[Class]] process
if (typeof process !== 'undefined' && Object.prototype.toString.call(process) === '[object process]') {
// For node use HTTP adapter
adapter = require('./adapters/http');
} else if (typeof XMLHttpRequest !== 'undefined') {
// For browsers use XHR adapter
adapter = require('./adapters/xhr');
}
return adapter;
}
var defaults = {
// 得到当前环境对应的请求适配器
adapter: getDefaultAdapter(),
// 请求转换器
transformRequest: [function transformRequest(data, headers) {
...
// 如果data是对象, 指定请求体参数格式为json, 并将参数数据对象转换为json
if (utils.isObject(data)) {
setContentTypeIfUnset(headers, 'application/json;charset=utf-8');
return JSON.stringify(data);
}
return data;
}],
// 响应数据转换器: 解析字符串类型的data数据
transformResponse: [function transformResponse(data) {
/*eslint no-param-reassign:0*/
if (typeof data === 'string') {
try {
data = JSON.parse(data);
} catch (e) {
/* Ignore */ }
}
return data;
}],
...
6. xhrAdapter()做了什么?
整体流程: request(config) ===> dispatchRequest(config) ===> xhrAdapter(config)
request(config): 将请求拦截器 / dispatchRequest() / 响应拦截器 通过promise链串连起来, 返回promise
dispatchRequest(config): 转换请求数据 == => 调用xhrAdapter()发请求 ===> 请求返回promise=>.then中对响应数据transformdata转换. 返回promise
xhrAdapter(config): 创建XHR对象, 根据config进行相应设置, 发送特定请求, 并接收响应数据, 返回promise
7. axios的请求/响应拦截器是什么?
请求拦截器: 在真正发请求前, 可以对请求进行检查或配置进行特定处理的函数, 包括成功/失败的函数, 传递的必须是config
响应拦截器: 在请求返回后, 可以对响应数据进行特定处理的函数, 包括成功/失败的函数, 传递的默认是response
8. axios的请求/响应数据转换器是什么?
请求转换器: 对请求头和请求体数据进行特定处理的函数
setContentTypeIfUnset(headers, 'application/json;charset=utf-8');
return JSON.stringify(data)
响应转换器: 将响应体json字符串解析为js对象或数组的函数
response.data = JSON.parse(response.data)
9. response的整体结构
{
data,
status,
statusText,
headers,
config,
request
}
10. error的整体结构
{
message,
request,
response
}
11. 如何取消已经发送的请求?
1.当配置了cancelToken对象时, 保存cancel函数
创建一个用于将来中断请求的cancelPromise
并定义了一个用于取消请求的cancel函数
将cancel函数传递出来
2.调用cancel()取消请求
执行cancel函数, 传入错误信息message
内部会让cancelPromise变为成功, 且成功的值为一个Cancel对象
在cancelPromise的成功回调中中断请求, 并让发请求的proimse失败, 失败的reason为Cancel对象
//html界面
// 添加请求拦截器
axios.interceptors.request.use((config) => {
// 在准备发请求前, 取消未完成的请求
if (typeof cancel==='function') {
cancel('取消请求')
}
// 添加一个cancelToken的配置
config.cancelToken = new axios.CancelToken((c) => {
// c是用于取消当前请求的函数
// 保存取消函数, 用于之后可能需要取消当前请求
cancel = c
})
return config
})
//xhr.js
// 如果配置了cancelToken
if (config.cancelToken) {
// 指定用于中断请求的回调函数
config.cancelToken.promise.then(function onCanceled(cancel) {
if (!request) {
return;
}
// 中断请求
request.abort();
// 让请求的promise失败
reject(cancel);
// Clean up request
request = null;
});
}
/**
* 用于取消请求的对象构造函数
*
* A `CancelToken` is an object that can be used to request cancellation of an operation.
*
* @class
* @param {Function} executor The executor function.
*/
function CancelToken(executor) {
if (typeof executor !== 'function') {
throw new TypeError('executor must be a function.');
}
// 为取消请求准备一个promise对象, 并保存resolve函数
var resolvePromise;
this.promise = new Promise(function promiseExecutor(resolve) {
resolvePromise = resolve;
});
// 保存当前token对象
var token = this;
// 立即执行接收的执行器函数, 并传入用于取消请求的cancel函数
executor(function cancel(message) {
// 如果token中有reason了, 说明请求已取消
if (token.reason) {
// Cancellation has already been requested
return;
}
// 将token的reason指定为一个Cancel对象
token.reason = new Cancel(message);
// 将取消请求的promise指定为成功, 值为reason
resolvePromise(token.reason);
});
}