原文地址javascript
總結一下使用
Axios.js
的遇到的注意事項,若有不對歡迎指正。java本文不是一篇分析源碼的文章,若是須要看源碼解析看這裏axios實例應用及源碼剖析 - xhr篇 (走心教程),寫的很是不錯。ios
先放張圖鎮樓,後續的問題須要多看看這張圖。git
以前沒有注意官網說明,後來升級了版本發現報錯,馬上把官網的文檔翻了一下,發現
Semver
章節有描述,github直到v1.0以前,當發佈一個minor版本,就表明有breaking changes了。因此升級有危險啊!!!typescript
攔截器是
axios.js
的核心了,就是有了攔截器作解耦,才能把代碼組合的更優雅。json
當初在寫攔截器時發現,官方給的例子,interceptor的返回值既能夠是普通的value值,又能夠是Promise。當時就疑惑了,有啥區別???axios
// 官方例子:
// Add a request interceptor
axios.interceptors.request.use(function (config) {
// Do something before request is sent
return config;
}, function (error) {
// Do something with request error
return Promise.reject(error);
});
// Add a response interceptor
axios.interceptors.response.use(function (response) {
// Do something with response data
return response;
}, function (error) {
// Do something with response error
return Promise.reject(error);
});
複製代碼
// 源碼裏面就有一行代碼
promise = promise.then(chain.shift(), chain.shift());
// 其實把上面例子翻譯一下就是這樣的
// Add a request interceptor
promise = promise.then(function (config) {
// Do something before request is sent
return config;
}, function (error) {
// Do something with request error
return Promise.reject(error);
});
// Add a response interceptor
promise = promise.then(function (response) {
// Do something with response data
return response;
}, function (error) {
// Do something with response error
return Promise.reject(error);
});
複製代碼
是否是瞬間就明白了攔截器的return值,其實能夠是普通的value值,或者Promise對象。promise
返回Promise就有不少想法了,咱們能夠在攔截器
interceptor
中作異步操做,終於能夠隨心所欲了 :)。
這個問題呢,是我在使用過程當中糾結的一個問題,我想作一個功能,就是當
Content-Type: application/x-www-form-urlencoded;charset=utf-8
時候,自動把data
使用qs.stringify
(後來知道這個功能其實使用transformer
更合適)。
// axios/lib/core/Axios.js
// 核心邏輯就是這段
var chain = [dispatchRequest, undefined]; // 正中間的是發送請求的攔截器
var promise = Promise.resolve(config);
// request 攔截器unshift,添加到數組的頭部
this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
chain.unshift(interceptor.fulfilled, interceptor.rejected);
});
// response 攔截器 push到數組尾部
this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
chain.push(interceptor.fulfilled, interceptor.rejected);
});
while (chain.length) {
// 循環執行攔截器
promise = promise.then(chain.shift(), chain.shift());
}
複製代碼
上述代碼其實就是作了兩件事情:
把攔截器放入數組chain中
this.interceptors.request
攔截器是從chain
的頭部放入;this.interceptors.request
其實也是數組,因此request
數組中最後放入的interceptor卻在chain數組的最前面,和axios.interceptors.request.use
時候的順序相反。this.interceptors.response
攔截器是放入chain
的尾部,因此interceptor順序和添加axios.interceptors.response.use
時候的順序相同
循環執行chain
中的攔截器
request
攔截器,最後添加的,最早執行
response
攔截器,按照添加的順序執行
注意:若是你的interceptors 之間順序有***前後依賴關係***,須要特別注意。
transformRequest
和transformResponse
都屬於Axios.js
的transformer,官方宣傳的Automatic transforms for JSON data 就是經過transformResponse
實現的。官方文檔寫的太簡單了,沒用寫要注意的問題。
默認處理JSON
// axios/lib/defaults.js
var defaults = {
adapter: getDefaultAdapter(),
transformRequest: [function transformRequest(data, headers) {
normalizeHeaderName(headers, 'Content-Type');
if (utils.isFormData(data) ||
utils.isArrayBuffer(data) ||
utils.isBuffer(data) ||
utils.isStream(data) ||
utils.isFile(data) ||
utils.isBlob(data)
) {
return data;
}
if (utils.isArrayBufferView(data)) {
return data.buffer;
}
if (utils.isURLSearchParams(data)) {
setContentTypeIfUnset(headers, 'application/x-www-form-urlencoded;charset=utf-8');
return data.toString();
}
if (utils.isObject(data)) {
setContentTypeIfUnset(headers, 'application/json;charset=utf-8');
return JSON.stringify(data);
}
return data;
}],
transformResponse: [function transformResponse(data) {
/*eslint no-param-reassign:0*/
if (typeof data === 'string') {
try {
data = JSON.parse(data);
} catch (e) { /* Ignore */ }
}
return data;
}],
};
複製代碼
// transformRequest
if (utils.isURLSearchParams(data)) {
setContentTypeIfUnset(headers, 'application/x-www-form-urlencoded;charset=utf-8');
return data.toString();
}
if (utils.isObject(data)) {
setContentTypeIfUnset(headers, 'application/json;charset=utf-8');
return JSON.stringify(data);
}
複製代碼
URLSearchParams
類型,axios把Content-Type
修改成application/x-www-form-urlencoded;charset=utf-8
,且序列化dataContent-Type
修改成application/json;charset=utf-8
,且序列化datatransformResponse
默認嘗試解析JSON內置的
transformRequest
和transformResponse
,很容易被覆蓋,若是你不須要內置的那就很好辦,若是你須要就須要注意下。
import axios from 'axios';
const defaults = { // 須要把默認的添加回來
transformRequest: [].concat(axios.defaults.transformRequest, (data, headers) => {
// do something
return data;
})
};
複製代碼
Content-Type
注意
URLSearchParams
兼容性問題,若是兼容IE 11,使用qs
處理下。
Content-Type
修改成application/x-www-form-urlencoded;charset=utf-8
,且序列化dataContent-Type
修改成application/json;charset=utf-8
,且序列化datatransformer和interceptor都能在請求過程當中發揮做用,有啥區別呢?
區別:
module.exports = function transformData(data, headers, fns) {
/*eslint no-param-reassign:0*/
utils.forEach(fns, function transform(fn) {
data = fn(data, headers);
});
return data;
};
複製代碼
axios內部判斷config.url,若是是絕對路徑開始就不添加baseUrl,不然就添加
// axios/lib/core/dispatchRequest.js
// Support baseURL config
if (config.baseURL && !isAbsoluteURL(config.url)) {
config.url = combineURLs(config.baseURL, config.url);
}
複製代碼
若是你的
transformer
和interceptor
對data
有變形或者使用,必定要先判斷二進制,否則容易致使報錯。 推薦的作法就是,使用單獨的axios
實例處理二進制數據。 多個axios
實例之間,注意共享headers
部分(這個坑,我已經踩進去了)。
以上是本人使用
axios.js
過程當中遇到的疑惑,而後經過查看源碼的總結,若是對你有幫助,我會感到很榮幸。
關注公衆號,發現更多精彩內容。
![]()