axios原始碼分析——請求流程

m_ing發表於2018-06-16

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

好了,忽略了各種細節,整個流程挺清楚的了!

小白第一次分析原始碼,各位湊合看吧!

關於攔截器的分析,傳送門,戳這裡

關於取消請求的分析,傳送門,戳這裡

相關文章