axios源码分析——请求流程

4,418 阅读4分钟

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环境。

好了,忽略了各种细节,整个流程挺清楚的了!

小白第一次分析源码,各位凑合看吧!

关于拦截器的分析,传送门,戳这里

关于取消请求的分析,传送门,戳这里