一、以下是对axios.js、Axios.js、InterceptorManager.js源码的解读:
axios.js文件:
'use strict';
// axios入口文件
// 引入工具
var utils = require('./utils');
// 引入绑定函数,创建函数
var bind = require('./helpers/bind');
// 引入Axios主文件
var Axios = require('./core/Axios');
// 引入合并配置文件
var mergeConfig = require('./core/mergeConfig');
// 导入默认配置
var defaults = require('./defaults');
/**
* Create an instance of Axios
* 创建一个Axios实例对象
* @param {Object} defaultConfig The default config for the instance
* @return {Axios} A new instance of Axios
*/
function createInstance(defaultConfig) {
// 创建一个实例对象 context可以调用get,put,post,delete,request
var context = new Axios(defaultConfig); // contest不能当函数使用
// 将request方法的this指向context并返回新函数,instance可以用作函数使用,切返回的是一个promise对象
var instance = bind(Axios.prototype.request, context);
// Copy axios.prototype to instance
// Axios.prototype和实例对象的方法都添加到instance函数身上
utils.extend(instance, Axios.prototype, context);
// Copy context to instance
// 将实例对象的方法和属性扩展到instance身上
utils.extend(instance, context);
return instance;
}
// Create the default instance to be exported
// 通过配置创建axios函数
var axios = createInstance(defaults);
// Expose Axios class to allow class inheritance
// axios添加Axios属性,属性值为构造函数对象,axios.CancelToken = CancelToken
axios.Axios = Axios;
// Factory for creating new instances
// 工厂函数,用来返回创建实例对象函数
axios.create = function create(instanceConfig) {
return createInstance(mergeConfig(axios.defaults, instanceConfig));
};
// 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);
};
axios.spread = require('./helpers/spread');
// 简单实现全局暴露axios
window.axios = axios
module.exports = axios;
// Allow use of default import syntax in TypeScript
module.exports.default = axios;
Axios.js文件:
'use strict';
var utils = require('./../utils');
var buildURL = require('../helpers/buildURL');
// 引入拦截器管理构造函数
var InterceptorManager = require('./InterceptorManager');
// 引入发送请求的函数
var dispatchRequest = require('./dispatchRequest');
// 获取合并配置的函数
var mergeConfig = require('./mergeConfig');
/**
* Create a new instance of Axios
* 创建Axios构造函数
* @param {Object} instanceConfig The default config for the instance
*/
function Axios(instanceConfig) {
// 实例对象上的 defaults属性为配置对象
this.defaults = instanceConfig;
// 实例对象上有interceptors属性用来设置请求和响应拦截器
this.interceptors = {
request: new InterceptorManager(),
response: new InterceptorManager()
};
}
/**
* Dispatch a request
* 发送请求方法,原型上配置,则实例对象就可以调用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);
// Set config.method
// 设置请求方法
if (config.method) {
config.method = config.method.toLowerCase();
} else if (this.defaults.method) {
config.method = this.defaults.method.toLowerCase();
} else {
config.method = 'get';
}
// Hook up interceptors middleware
//创建拦截器中间件,第一个参数用来发送请求,第二个为undefined用来补位。
var chain = [dispatchRequest, undefined];
//创建一个成功的promise切成功的值为合并后的请求配置
var promise = Promise.resolve(config);
// 遍历实例对象的请求拦截器
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);
});
// 如果链条长度不为0
while (chain.length) {
// 依次取出chain的回调函数,并执行
promise = promise.then(chain.shift(), chain.shift());
}
return promise;
};
Axios.prototype.getUri = function getUri(config) {
config = mergeConfig(this.defaults, config);
return buildURL(config.url, config.params, config.paramsSerializer).replace(/^\?/, '');
};
// Provide aliases for supported request methods
utils.forEach(['delete', 'get', 'head', 'options'], function forEachMethodNoData(method) {
/*eslint func-names:0*/
Axios.prototype[method] = function(url, config) {
return this.request(utils.merge(config || {}, {
method: method,
url: url
}));
};
});
utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {
/*eslint func-names:0*/
Axios.prototype[method] = function(url, data, config) {
return this.request(utils.merge(config || {}, {
method: method,
url: url,
data: data
}));
};
});
module.exports = Axios;
注:
axios和$原理是一样的,它们先造一个函数出来,然后在往函数身上添加方法,然后我们使用的时候,既可以当函数用也可以调方法使用。
InterceptorManager.js文件:
'use strict';
// 拦截器管理器构造函数
var utils = require('./../utils');
/**
* 声明构造函数
* axios.interceptors.request.use
* axios.interceptors.response.use
*/
function InterceptorManager() {
// 创建一个属性
this.handlers = [];
}
/**
* Add a new interceptor to the stack
* 添加拦截器到栈中,以待后续执行,返回拦截器的编号(编号为当前拦截器综合数减一)
* @param {Function} fulfilled The function to handle `then` for a `Promise`
* @param {Function} rejected The function to handle `reject` for a `Promise`
*
* @return {Number} An ID used to remove interceptor later
*/
InterceptorManager.prototype.use = function use(fulfilled, rejected) {
this.handlers.push({
fulfilled: fulfilled,
rejected: rejected
});
return this.handlers.length - 1;
};
/**
* Remove an interceptor from the stack
* 从拦截器数组中移除制定ID的拦截器
* @param {Number} id The ID that was returned by `use`
*/
InterceptorManager.prototype.eject = function eject(id) {
if (this.handlers[id]) {
this.handlers[id] = null;
}
};
/**
* Iterate over all the registered interceptors
* 创建拦截器对象遍历方法
* This method is particularly useful for skipping over any
* interceptors that may have become `null` calling `eject`.
*
* @param {Function} fn The function to call for each interceptor
*/
InterceptorManager.prototype.forEach = function forEach(fn) {
utils.forEach(this.handlers, function forEachHandler(h) {
if (h !== null) {
fn(h);
}
});
};
module.exports = InterceptorManager;
二、手写实现axios
// 构造函数
function Axios(config){
// 初始化
this.defaults = config;
this.interceptors = {
request:{},
response:{}
}
}
// 原型添加相关方法
Axios.prototype.request = function(config){
console.log("发送Ajax请求,请求类型为:" + config.method)
}
Axios.prototype.get = function(config){
return this.request({
method:'GET'
})
}
Axios.prototype.post = function(config){
return this.request({
method:'POST'
})
}
// 声明函数
function createInstance(config){
// 实例化一个对象
let context = new Axios(config); // axios.get()...,但是不能当做函数使用,例如context();
// 创建请求函数
var instance = Axios.prototype.request.bind(context); // instance是一个函数并且可以instance({}),此时instance不能当做对象使用,例如:instance.get()
// 将Axios.prototype对象中的方法添加到instance函数对象中
Object.keys(Axios.prototype).forEach(key=>{
console.log(key,"key");
instance[key] = Axios.prototype[key].bind(context); // 可以使用this.default,this.interceptors
})
// 为instance函数对象添加default和interceptors
Object.keys(context).forEach(key=>{
console.log(key,"key");
instance[key] = context[key]
})
// console.dir(instance);
return instance;
}
let axios = createInstance();
axios({method:'POST'})
axios.get()
axios.post()
三、模拟axios实现过程
// 1.声明构造函数
function Axios(config){
this.config = config
}
Axios.prototype.request = function(config){
// 创建一个promise对象
let promise = Promise.resolve(config);
// 声明一个数组
let chains = [dispatchRequest,undefined];
// 调用then方法制定回调
let result = promise.then(chains[0],chains[1]);
return result;
}
// 2.dispatchRequest函数,返回结果是一个promise对象
// 返回promise对象
function dispatchRequest(config){
console.log("config",config);
// 调用适配器发送请求
return xhrAdapter(config).then(response =>{
console.log(response);
// 对响应的结果进行转换处理
return response; //暂时不做处理
},error =>{
console.log(error);
throw error;
});
}
// 3.adapter适配器
function xhrAdapter(config){
console.log("adapter",config);
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 && xhr.status < 300){
resolve({
// 配置对象
config:config,
// 响应体
data:xhr.response,
// 响应头
headers:xhr.getAllResponseHeaders(),
// xhr请求对象
request:xhr,
// 响应状态码
status:xhr.status,
// 响应状态字符串
statusText:xhr.statusText
})
}else{
reject(new Error('请求失败'));
}
}
}
})
}
// 4.创建axios函数
let axios = Axios.prototype.request.bind(null);
axios({
method:'GET',
url:'http://localhost:3000/posts'
}).then(res=>{
console.log(res)
})
四、模拟拦截器实现过程
//构造函数
function Axios(config){
this.config = config;
this.interceptors = {
request: new InterceptorManager();
response: new InterceptorManager()
}
}
Axios.prototype.request = function(config){
let promise = Promise.resolve(config);
// 创建chain数组
let chains = [dispatchRequest,undefined];
// 处理拦截器
// 请求拦截器 将它的回调压入chain的前面
this.interceptors.request.handlers.forEach(item =>{
chains.unshift(item.fulfilled,item.rejected)
})
// 请求拦截器 将它的回调压入chain的尾部
this.interceptors.response.handlers.forEach(item =>{
chains.push(item.fulfilled,item.rejected)
})
console.log(chains);
while(chains.length > 0){
promise = promise.then(chains.shift(),chains.shift())
}
return promise
}
function dispatchRequest(config){
return new Promise((resolve, reject)=>{
resolve({
state:200,
statusText:'success'
})
})
}
// 创建实例
let context = new Axios({});
let axios = Axios.prototype.request.bind(context);
// 将context 属性 config 和 interceptors添加至axios函数对象身上
Object.keys(context).forEach(key =>{
axios[key] = context[key]
})
// 拦截器管理器构造函数
function InterceptorManager(){
this.handlers = [];
}
// 拦截器管理器原型
InterceptorManager.prototype.use = function(fulfilled, rejected){
this.handlers.push({
fulfilled,
rejected
})
}
// 设置请求拦截器,config配置对象
axios.interceptors.request.use(function(config){
console.log('请求拦截器');
return config;
}, function(error){
console.log('请求拦截器');
return Promise.reject(error);
})
// 设置响应拦截器,config配置对象
axios.interceptors.response.use(function(config){
console.log('响应拦截器');
return config;
}, function(error){
console.log('响应拦截器');
return Promise.reject(error);
});
console.dir(axios);
// 发送请求
axios({
url: 'http://localhost:3000/posts',
method: 'post',
data: {
username: 'admin',
password: '123456'
}
}).then(function(res){
console.log(res);
}).catch(function(err){
console.log(err);
})
五、总结
5.1、axios与Axios的关系?
- 从语法上说:
axios不是Axios的实例 - 从功能上说:
axios是Axios的实例 axios是Axios.proptype.request函数bind()返回的函数axios作为对象有Axios原型对象上所有方法,有Axios对象上所有属性
5.2、instance与axios的区别?
相同点:
- 都是一个能发任意请求的函数:
request(config) - 都有发特定请求的各种方法:
get()/post()/put()/delete() - 都有默认配置和拦截器的属性:
defaults/interceptors
不同点:
- 默认配置很可能不一样
instance没有axios后面添加的一些方法:create()/CancelToken()
5.3、axios整体流程
- 整体流程
request(config) ==> dispatchRequest(config) ==> xhrAdapter(config)
request(config)
将请求拦截器/dispatchRequest()/响应拦截器 通过promise链串连起来,返回promise
dispatchRequest(config)
转换请求拦数据 ==> 调用xhrAdapter()请求 ==> 请求返回后转换响应数据,返回promise
xhrAdapter(config)
创建XHR对象,根据config进行相应设置,发送特定请求,并接收响应数据,返回promise
5.4、axios的请求/响应拦截器是什么?
- 请求拦截器
- 在真正发送请求前执行的回调函数
- 可以对请求进行检查或配置进行特定处理
- 成功的回调函数,传递的默认是
config(也必须是) - 失败的回调函数,传递的默认是
error
- 响应拦截器
- 在请求得到响应后执行的回调函数
- 可以对响应数据进行特定处理
- 成功的回调函数,传递的默认是
response - 失败的回调函数,传递的默认是
error
5.5、axios的请求/响应数据转换器是什么?
- 请求转换器:对请求头和请求体数据进行特定处理的函数
if (lutils.isObject(data)) {
setContentTypelfUnset(headers, 'application/json;charset=utf-8')
return JSON.stringify(data);
}
- 响应转换器:将响应体json字符串解析为js对象或数组的函数
response.data = JSON.parse(response.data)
5.6、response整体结构
data,
status,
statusText,
headers,
config
request
5.7、Error整体结构
message,
response,
request,
5.8、如何取消未完成的请求?
- 当配置了
cancelToken对象时,保存cancel函数
- 创建一个用于将来中断请求的
cancelPromise - 并定义了一个用于取消请求的
cancel函数 - 将
cancel函数传递出来
- 调用
cancel()取消请求
- 执行
cacel函数,传入错误信息message - 内部会让
cancelPromise变为成功的值为一个Cance对象 - 在
cancelPromise的成功回调中中断请求,并让发请求的proimse失败,失败的reason为Cancel对象