axios就不在里介绍了,直接步入正题,先从一个最基本的get请求来分析下源码。
axios.get('/get?name=xmz')
.then((response)=>{
console.log('response', response)
})
.catch((error)=>{
console.log('error', error)
})
复制代码
先从axios这个对象看,暂且认为它是一个普通的对象,加载axios文件后,直接就可以使用,所以就直接去文件里找它去~~~
找到axios/lib/axios.js
就直接看到了var axios躺在哪里。
var axios = createInstance(defaults);
复制代码
先不管defaults就当是一个默认的对象,向上看就看到了createInstance方法
function createInstance(defaultConfig){
var context = new Axios(defaultConfig);
//new Axios()这么说context 是一个对象了
var instance = bind(Axios.prototype.request, context);
//bind()要对这个对象干什么???
//...下面的就先不看了
return instance;
}
复制代码
再扯远点,看看bind方法到底对context干什么了?bind定义在axios/lib/helps/bind.js
这个目录下
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,就是一个函数。
既然axios是一个函数,那么这个函数的get属性在哪定义的呢?然后我就在整个axios/lib
文件夹下简单粗暴地搜了下axios.get,然而并没有找到~~~ 找了半天没找到啊!!抓狂!!但是在来回翻源码的时候看到Axios.prototype上有get属性,但是axios却不是Axios的实例化对象啊,怎么办?
此时发现在var axios之后,axios马上就已经有get属性了(写了个for in 循环偷偷看了下),那么就顺着向上找把,还是找到了createInstance方法
function createInstance(){
//...
//刚才有两行省略掉没看 好后悔!
//看到extend 好激动
utils.extend(instance, Axios.prototype, context);
utils.extend(instance, context)
return instance
}
复制代码
这下知道了,axios的get属性原来是从Axios.prototype上继承来的,那就去看看utils.entend是怎么工作的吧!进入了axios/lib/util.js
文件。
function extend(a, b, thisArg){
// extend就是把b的属性都复制个a
forEach(b, function(value, key){
//如果属性的值是一个方法的话,就改变this的指向到thisArg再复制给a
if(thisArg && typeof value == 'function'){
a[key] = bind(val, thisArg)
}else{
a[key] = value;
}
})
return a
}
复制代码
axios继承了Axios.prototype和context的所有的属性,属性值是方法的话,里面的this都是指向context的。 这回就可以去看Axios.prototype.get了,不去搜了,我看到它不是直接定义的了!哈哈哈!在axios/lib/core/Axios.js
里
utils.forEach(['delete', 'get', 'head', 'option'], function(method){
Axios.proptotype[method] = function(url, config){
// 先不去看util.merge了,我猜也就是把config 和 后面的对象进行下合并
return this.request(util.merge(config||{}, {
method: method,
url: url
}))
}
})
复制代码
Axios.prototype.get()其实去执行的是Axios.prototype.request(),还在这个文件里,向上看
Axios.prototype.request = function request(config){
// ...
// 开始就对config进行判断,合并默认配置
var chain = [dispatchRequest, undefined]
// dispatchRequest是什么一会再说
var promise = Promise.resolve(config)
// Promise.resolve一个对象,promise就是一个立即resolve的Promise对象
// ...
// 关于interceptors拦截器的东西先不看了
while(chain.length){
promise = promise.then(chain.shift(), chain.shift())
}
return promise
}
复制代码
返回的是一个promise这就和axios.get('/get?name=xmz').then().catch()对上了。暂且不考虑拦截器,经过这个while循环其实就是去执行了dispatchRequest(config),那么就去axios/lib/core/dispatchRequest.js
看下吧
function dispatchRequest(config){
// ...
// 开始就对config的 baseUrl, data, headers进行处理
var adapter = config.adapter || defaults.adapter;
// 这个adapter是什么?一路以来并没有关注啊?
// 执行adapter是一个Promise对象,resolve的函数的参数还是response
// adpater肯定是去发送请求了啊
return adapter(config).then(function(response){
// ...
return response
}, function(reason){
// ...
return Promise.reject(reason)
})
}
复制代码
既然config.adapter没看见,就去axios/lib/default.js
看defaults.adapter吧
var defaults.adapter = getDefaultAdapter();
function getDefaultAdapter(){
var adapter;
if(typeof XMLHttpRequest !== 'undefined'){
// 浏览器环境
adapter = require('./adapter/xhr');
}else if(typeof process !== 'undefined'){
// node环境
adapter = require('./adapter/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环境。
好了,忽略了各种细节,整个流程挺清楚的了!