最近事情多,很久沒看jquery的源碼了(唉),而且後面排的東西越來越多,暫時應該不會繼續,下次找個機會再重新撿起來吧
因爲用到的工程裏包裝了axios,並且還踩了次坑,所以拿來看了看
自己比較感興趣的特性如下:
- 請求和返回的攔截
- 取消請求
- 請求代理
axios用到的包很少,只有url和follow-redirects,它的源碼在lib文件夾下,lib下面又有adapters、cancel、core和helpers四個文件夾和axios、defaults、utils三個文件。
和有些工程不同的是,lib下面的每個目錄都有readme,簡要介紹了當前的文件夾是幹什麼用的
好了,閒話說完,看主要內容
先按大體結構來看一下:
- 最外層
- axios.js是入口,主要工作是創建Axios類的實例,並擴展了一些方法
- defaults是一些默認配置項,可以直接看git上面的註釋,很齊全
- utils裏是一些工具方法
- core文件夾
- core文件夾裏的Axios纔不到100行,主要是提供了request方法,並且把’delete’, ‘get’, ‘head’, ‘options’(不帶數據)和’post’, ‘put’, ‘patch’(帶數據)這幾個方法綁到request上,request方法很簡短,但是實現了請求和返回的攔截(通過promise.then,一個數組和數組的push、unshift方法,非常清奇)
- dispatchRequest裏處理了request data和response data的格式處理
- 另外幾個感覺沒啥可說的
- cancel文件夾
感覺大概是最難的了,CancelToken裏有個同名構造函數和一個source函數,說實話,看了很多遍都沒看懂,只理一個流程吧,先看官方給的cancel的用法,
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
axios.get('/user/12345', {
cancelToken: source.token
}).catch(function(thrown) {
if (axios.isCancel(thrown)) {
console.log('Request canceled', thrown.message);
} else {
// handle error
}
});
axios.post('/user/12345', {
name: 'new name'
}, {
cancelToken: source.token
})
// cancel the request (the message parameter is optional)
source.cancel('Operation canceled by the user.');
流程如下:
a. 執行CancelToken裏的source方法,沒幹啥,創建了一個CancelToken實例,傳遞的參數是一個函數,這個函數把自己的參數傳遞給了cancel這個變量,然後返回了cancel和這個CancelToken實例
CancelToken.source = function source() {
var cancel;
var token = new CancelToken(function executor(c) {
cancel = c;
});
return {
token: token,
cancel: cancel
};
};
然後看一眼CancelToken的構造函數幹了啥,創建了一個promise,把executor參數執行了一下,這個執行結果就是上面source方法裏面的cancel變成了executor的參數:function cancel(message)
,那麼後面執行source.cancel('Operation canceled by the user.');
這句的時候,其實就是執行了resolvePromise(token.reason);
function CancelToken(executor) {
if (typeof executor !== 'function') {
throw new TypeError('executor must be a function.');
}
var resolvePromise;
this.promise = new Promise(function promiseExecutor(resolve) {
resolvePromise = resolve;
});
var token = this;
executor(function cancel(message) {
if (token.reason) {
// Cancellation has already been requested
return;
}
token.reason = new Cancel(message);
resolvePromise(token.reason);
});
}
b. a裏面其實還只執行到const source = CancelToken.source();
這句,接下來就是執行請求,執行請求就走到了core裏的Axios.prototype.request裏,比較重要的兩句
...
var chain = [dispatchRequest, undefined];
... //這裏會插入請求和返回的攔截器
while (chain.length) {
promise = promise.then(chain.shift(), chain.shift());
}
排除攔截器的影響,最後會執行到dispatchRequest,dispatchRequest方法的第一句就執行了判斷是否要取消請求的操作throwIfCancellationRequested(config);
,這一句最終執行到
CancelToken.prototype.throwIfRequested = function throwIfRequested() {
if (this.reason) {
throw this.reason;
}
};
拋出一個錯誤,結束。這是上面官方例子的取消流程,平時使用的時候,還有一種方式(同事提供),用一個變量來保存cancelToken,然後檢測這個變量是否已存在,在有多次發送請求的情況下,可以取消發送過程中的請求,這樣的流程和官方例子會有點不一樣,因爲官方例子是直接在請求前進行取消的,而這種情況會走到adapter裏面去取消(在adapter文件夾中搜索cancelToken就可以找到)。
4. adapter文件夾
兩種發送請求的方式,一種瀏覽器的ajax方式,一種是node的http方式,其中只有http方式支持請求代理,當然了,如果自己想加一個ajax方式的代理,也是可以的,模仿下代碼就行了,代理其實也不是很高深的東西,只是把請求的host和port給改了,官方實例如下:
proxy: {
host: '127.0.0.1',
port: 9000,
auth: {
username: 'mikeymike',
password: 'rapunz3l'
}
},
上面這一段直接寫在請求的配置裏就可以了,不過自己手寫的代理服務器的話,需要支持對應的請求路徑,被這個坑了一臉,比如說,我請求http://127.0.0.1:3000/api?username=123,設置代理127.0.0.1:3001,如果3001上沒有api這個路徑的監聽,是不會成功的
通過這次看axsios的代碼,大概收穫了以下這些以前沒見過的知識點:
- URLSearchParams(兼容性不好)
- XDomainRequest(已經是不支持的過時的東西了)
- 204狀態碼(提交form的時候不刷新頁面也不跳轉)
axios前前後後大概看了得有五六回,基本都是在看請求取消的環節,非常費解,大概是水平太差,各個文件裏的promise有點多也比較容易混淆