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環境。
好了,忽略了各種細節,整個流程挺清楚的了!
小白第一次分析原始碼,各位湊合看吧!
關於攔截器的分析,傳送門,戳這裡。
關於取消請求的分析,傳送門,戳這裡。