axios 是一个基于 promise 的 HTTP 库,能够用在浏览器和 node.js 中。本质上也是对原生XHR的封装。node
axios.defaults.baseURL = 'http://xxx.com/api';
axios.defaults.baseURL = 'https://api.example.com';
axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
axios.interceptors.request.use(resolveFn1, rejectFn2); // 添加请求拦截器
axios.interceptors.response.use(resolveFn2, rejectFn2); // 添加响应拦截器
axios('/user?ID=12345').then(() => {
// 请求成功的处理
}, () => {
// 请求异常的处理
}
);
axios.get('/user?ID=12345').then(function (response) {
//
}).catch(function (error) {
//
}).finally(function () {
//
});
复制代码
加载axios文件后,能够直接使用axios.get,能够把axios看做是一个对象,找到axios/lib/axios.js
文件,就能够直接看到var axiosios
var axios = createInstance(defaults);
复制代码
defaults是一个对象,一些默认配置值ajax
{
adapter: ƒ xhrAdapter(config)
headers: {
common: { Accept: "application/json, text/plain, */*" },
delete: {},
get: {},
head: {},
patch:{ Content-Type: "application/x-www-form-urlencoded" }
post: { Content-Type: "application/x-www-form-urlencoded" }
put: { Content-Type: "application/x-www-form-urlencoded" }
}
maxContentLength: -1
timeout: 0
transformRequest: [ƒ]
transformResponse: [ƒ]
validateStatus: ƒ validateStatus(status)
xsrfCookieName: "XSRF-TOKEN"
xsrfHeaderName: "X-XSRF-TOKEN"
}
复制代码
createInstance方法具体实现json
function createInstance(defaultConfig) {
//new Axios的实例,context就是一个对象了
var context = new Axios(defaultConfig);
//instance是一个函数,是Axios.prototype.request方法
var instance = bind(Axios.prototype.request, context);
// 复制Axios.prototype到instance
utils.extend(instance, Axios.prototype, context);
//复制context到instance
utils.extend(instance, context);
return instance;
}
复制代码
先看看Axios构造函数是什么,建立了一个怎么样的实例。在axios/lib/core/Axios.js
中axios
function Axios(instanceConfig) {
//两个属性
this.defaults = instanceConfig;
this.interceptors = {
request: new InterceptorManager(),
response: new InterceptorManager()
};
}
//原型对象上方法
Axios.prototype.request = function(){...}
Axios.prototype.getUri = function(){...}
utils.forEach(['delete', 'get', 'head', 'options'], function forEachMethodNoData(method) {
Axios.prototype[method] = function(url, config) {
return this.request(utils.merge(config || {}, {
method: method,
url: url
}));
};
});
复制代码
构造函数Axios.prototype上,终于看到有get方法的定义,get方法最终仍是调的request方法。context是Axios的实例,可是axios却不是Axios的实例化对象api
那instance又是什么呢?如何将axios和context对应上呢?promise
接着往下看bind浏览器
function bind(fn, thisArg) {
return function wrap() {
var args = new Array(arguments.length);
for (var i = 0; i < args.length; i++) {
args[i] = arguments[i];
}
return fn.apply(thisArg, args);
};
};
复制代码
bind返回一个函数wrap,暂且不看里面得话,咱们就能够知道instance是一个函数,那么当instance执行的时候,其实就是执行Axios.proptotype.request,this指向context,说白了bind就是改变了this指向,createInstance函数返回就是这个instance,因此axios就是instance,就是一个函数
bash
function createInstance(defaultConfig) {
//...
// 复制Axios.prototype到instance
utils.extend(instance, Axios.prototype, context);
//复制context到instance
utils.extend(instance, context);
return instance;
}
复制代码
咱们探探extend究竟作了什么,找到axios/lib/utils.js
服务器
// extend就是把b的属性都复制个a
function extend(a, b, thisArg) {
forEach(b, function assignValue(val, key) {
//若是属性的值是一个方法的话,就改变this的指向到thisArg再复制给a
if (thisArg && typeof val === 'function') {
a[key] = bind(val, thisArg);
} else {
a[key] = val;
}
});
return a;
}
复制代码
instance继承了Axios.prototype和context的全部的属性,属性值是方法的话,里面的this都是指向context的。instance就是axios。
回到最开始axios.get的调用,其实就是调用Axios.prototype.get。Axios.prototype.get内部最终调用Axios.prototype.request
Axios.prototype.request = function request(config) {
//...
//开始就对config进行判断,合并默认配置
var chain = [dispatchRequest, undefined];
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);
});
while (chain.length) {
promise = promise.then(chain.shift(), chain.shift());
}
return promise;
};
复制代码
Axios.prototype.request返回promise,因此能够链式调用axios.get(''/user?ID=12345'').then().catch()。
while循环执行dispatchRequest,继续看看它究竟作了什么? 找到axios/lib/core/dispatchRequest.js
文件
function dispatchRequest(config){
// ...
// 开始就对config的 baseUrl, data, headers进行处理
var adapter = config.adapter || defaults.adapter;
// 执行adapter是一个Promise对象,resolve的函数的参数仍是response
// adpater确定是去发送请求了啊
return adapter(config).then(function(response){
// ...
return response
}, function(reason){
// ...
return Promise.reject(reason)
})
}
复制代码
调用了adapter,config中没有,看defaults.adapter。进入到axios/lib/defaults.js
文件
var defaults = {
adapter: getDefaultAdapter(),
//...
}
function getDefaultAdapter() {
var adapter;
if (typeof XMLHttpRequest !== 'undefined') { //浏览器环境
adapter = require('./adapters/xhr');
} else if (typeof process !== 'undefined' && Object.prototype.toString.call(process) === '[object process]') {
//node环境
adapter = require('./adapters/http');
}
return adapter;
}
复制代码
adapter是区分浏览器和node环境的,那么咱们就去axios/lib/adapter/xhr.js
看下浏览器环境的,就是封装了一个promise进行ajax请求服务器。
至此回过头来再看开始那个get请求,axios.get继承于Axios.prototype.get,其实就是去执行Axios.prototype.request,返回一个promsie对象,因此可使用then和catch接收返回值和处理错误。进行ajax请求的主要函数就是adapter,adapter区分了一下浏览器和node环境
为何不将全部方法在Axios上实现而后返回new Axios呢?
根据源码可知,axios实例(instance)是对Axios.prototype.request方法包裹了一层函数,主要是为将Axios.prototype.request内部的this绑定到新建的Axios对象(context)上。而后经过 utils.extend 将内部context和Axios.prototyp的属性添加到这个Axios.prototype.request方法(intance)上,添加上去的函数也会绑定this到新建的Axios对象(context)上。最终的axios实例上面的方法内部的this指向的都是新建的Axios对象(context),从而使得不一样axios实例之间隔离了做用域,能够对每一个axios实例设置不一样的config。
axios('/user?ID=12345').then(() => {
// 请求成功的处理
}, () => {
// 请求异常的处理
}
);
复制代码
由于axios内部调用的都是Axios.prototype.request方法,Axios.prototype.request默认请求方法为get。为了让开发者能够直接axios(config)就能够发送get请求,而不须要axios.get(config)。若是直接new一个Axios对象是没法实现这种简写的(没错,就是为了少打几个字)
function request(config) {
if (typeof config === 'string') {
config = arguments[1] || {};
config.url = arguments[0];
} else {
config = config || {};
}
//合并参数
config = mergeConfig(this.defaults, config);
if (config.method) {
config.method = config.method.toLowerCase();
} else if (this.defaults.method) {
config.method = this.defaults.method.toLowerCase();
} else {
config.method = 'get'; //默认调用get方法
}
//...
复制代码
实际上axios.post、axios.put等全部axios的请求方法内部都是调用Axios.prototype.request