axios
是在實際項目中應用比較多的請求插件了,提供給咱們的前置請求配置、響應攔截配置
,讓咱們在實際應用中可以很好的利用;
node
不知道你們有沒有這種時候,當身邊的其餘人都在看源碼實現時候,本身卻無從下手,看着超級多的邏輯,逐漸失去了興趣,所以我發現我本身的看源碼切入點,「好奇」
,看着項目配置中的「響應攔截」、「請求攔截」等,由心而生想要去知道這樣究竟是如何實現的呢? 帶着這種「好奇」我打開了axios的源碼。ios
在看源碼以前,必要的內容是先過一遍axios的使用方法,這樣帶着問題,會記憶深入ajax
axios
的配置大概分爲三種axios
即在插件內部的默認配置api
在進行項目搭建時候,一般使用的自定義的全局配置數組
有的時候 在一個應用中須要實例化不一樣的kxios
對象,針對不一樣的接口 有利於管理,每一個實例化對象度灰有本身的配置,能夠經過全局配置進行初始化,或合併成一個新的配置項目,實例配置通常是咱們進行本身配置的。promise
const nAxios = axios.create({
// baseURL: process.env.BASE_API,
// timeout: 5000,
withCredentials: true,
headers: {'X-Requested-With': 'XMLHttpRequest'}
})
複製代碼
在進行請求時候,對單個請求進行的配置瀏覽器
同一個實例會有一些公用的配置項目,如baseUrl,可是不少時候,不一樣的請求具體的配置是不同的,如url、method
等,因此在請求的時候須要傳入的配置與實例配置進行合併; 請求配置合併通常是這樣子的?get請求的url ,後面的config都是咱們傳入的請求配置,傳入到項目中後,會merge
默認的配置,而後在進行請求;緩存
kxios.get('http://localhost:4000/',{
baseURL:'http://localhost:4000/',
methods:'get',
headers:{
'Content-Type':'333',
'Type':333
}
}).then((res)=>{
console.log("請求後的then")
console.log(res)
})
複製代碼
請求配置
>實例配置
> 全局配置
markdown
axios.create = function create(instanceConfig) {
return createInstance(mergeConfig(axios.defaults, instanceConfig));
};
複製代碼
該文件提供了axios的實例構造器,即工廠函數
function createInstance(defaultConfig) {
var context = new Axios(defaultConfig);
// instance 綁定axios的默認數據 instance
var instance = bind(Axios.prototype.request, context);
// 複製axios的原型到實例中。此處主要是確保後續進行改變時候,不改變Axios的對象函數
utils.extend(instance, Axios.prototype, context);
//將實例綁定到Axios構造出得對象中
utils.extend(instance, context);
//instance 進行復制
return instance;
}
複製代碼
Axios
的實例instance
綁定axios的默認數據instance
axios
的原型到實例中。此處主要是確保後續進行改變時候,不改變Axios
的對象函數Axios
構造出得對象中之因此選擇這樣,建立實例,而後在複製原型方法,是爲了擴展axios自己更多的調用功能,在Axios
的函數中,具備複雜數據類型,保證每一個axios的實例在繼承後都有獨立的數據結構;
axios.get(url)
axios(url)
複製代碼
在外界引用了axios
的方法就能進行建立axios的實例,並獲得初始的默認值 進行調用
var nAxios = axios.create({
baseURL: process.env.BASE_API,
timeout: 5000,
withCredentials: true,
headers: {'X-Requested-With': 'XMLHttpRequest'}
})
//調用函數
nAxios.get('http:locallhost:888')
複製代碼
對於nAxios.get('http:locallhost:888')
的執行的過程
Axios
的原型都附加上了請求方法,請求調用的時候直接調用時候,先進行merge
請求的配置,在進行調用config
的方法utils.forEach(['delete', 'get', 'head', 'options'], function forEachMethodNoData(method) {
/*eslint func-names:0*/
Axios.prototype[method] = function(url, config) {
return this.request(utils.merge(config || {}, {
method: method,
url: url
}));
};
});
複製代碼
請求時候進行請求的調度,即去適配請求
在瀏覽器環境中主要是經過ajax進行請求
在瀏覽器中進行發送請求主要仍是以來XMLHttpRequest
咱們在實際的使用中,發現node環境和瀏覽器環境均可以使用axios,實際上是axios內部進行了適配;
//獲取默認的請求適配器
function getDefaultAdapter() {
var adapter;
//若是當前存在XMLHttpRequest 這個對象,則進行XHr的額適配器
if (typeof XMLHttpRequest !== 'undefined') {
// 瀏覽器環境下使用XML這個適配器
adapter = require('./adapters/xhr')
} else if (typeof process !== 'undefined' && Object.prototype.toString.call(process) === '[object process]') {
// node 環境下使用http這個模塊適配器
adapter = require('./adapters/http');
}
return adapter;
}
複製代碼
請求攔截器和響應攔截器幫助咱們在實際工做中去處理本身前置轉換需求
/axios/lib/core/InterceptorManager.js
在該文件中定義了攔截器的構造函數InterceptorManager
,用於響應攔截和請求攔截的使用
function InterceptorManager() {
this.handlers = [];
}
//增長一個響應
InterceptorManager.prototype.use = function use(fulfilled, rejected) {
this.handlers.push({
fulfilled: fulfilled,
rejected: rejected
});
return this.handlers.length - 1;
};
//移除某一個
InterceptorManager.prototype.eject = function eject(id) {
if (this.handlers[id]) {
this.handlers[id] = null;
}
};
複製代碼
應用在Axios的構造函數中
function Axios(instanceConfig) {
this.defaults = instanceConfig;
this.interceptors = {
request: new InterceptorManager(), //請求攔截器
response: new InterceptorManager() // 響應攔截器
};
}
複製代碼
請求攔截器和響應攔截器主要實現原理爲Promise的鏈式執行方法;
// 攔截器中間件
//dispatchRequest爲默認的主要請求內容 dispatchRequest返回一個promise的請求對象
var chain = [dispatchRequest, undefined];
//讓config配置promise中,用來層級傳遞配置
var promise = Promise.resolve(config);
//請求攔截器
this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
//將請求攔截器插入到 chain數組的前面,插入的原理實際上是 自定義配置時候越在後配置的先執行
chain.unshift(interceptor.fulfilled, interceptor.rejected);
});
// 響應攔截器 將響應攔截器push 到chain 的後面 這樣會後執行
this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
chain.push(interceptor.fulfilled, interceptor.rejected);
});
//chain的結構 =[請求攔截器1,請求攔截器2,主要請求,響應攔截器1,響應攔截器2]
//執行chain中的方法
while (chain.length) {
promise = promise.then(chain.shift(), chain.shift());
}
複製代碼
這樣就造成了一個攔截器的Promise隊列
promise的then中 dispatchRequest.js
轉換函數
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/lib/cancel
中,取消請求在配置中主要使用CancelToken進行控制,在請求中,也存在專門爲CancelToken設置的方法, 在xhr.js中
if (config.cancelToken) {
// Handle cancellation
config.cancelToken.promise.then(function onCanceled(cancel) {
if (!request) {
return;
}
//中止當前的請求
request.abort();
//而且進行reject
reject(cancel);
// Clean up request
request = null;
});
}
複製代碼
cancelToken
的方法 中主要的一個做用在於執行阻止請求,並進行取消函數,進行下一個then的調用
看完axios ,本身收穫仍是蠻多的,更多的是流程設計