項目中一直都有用到 Axios 作為網(wǎng)絡請求工具,用它更要懂它,因此為了更好地發(fā)揮 Axios 在項目的價值,以及日后能夠得心應手地使用它,筆者決定從源碼層面好好欣賞一下它的美貌!

Axios是一款基于 Promise 并可用于瀏覽器和 Node.js 的網(wǎng)絡請求庫。
- Github:https://github.com/axios/axios
- NPM:https://www.npmjs.com/package/axios
- Docs:https://axios-http.com/docs/intro
最近,Axios 官方文檔終于變好看了,支持多語言切換,閱讀更清晰,使用起來也更加舒適!作為一款受全球歡迎的網(wǎng)絡請求庫,有必要偷學一下其中的架構設計、編碼方式。
本篇文章從源碼層面主要分析 Axios 的功能實現(xiàn)、設計模式、以及分享 Axios 中一些筆者認為比較“精彩”的地方!
本文主要內(nèi)容結構如下,大家按需食用:

一、Axios 項目概況
本次分析的 Axios 版本是:v0.24.0
通過簡單的瀏覽 package.json、文件及目錄,可以得知 axios 工程采用了如下三方依賴:
| 名稱 | 說明 |
|---|---|
| Grunt |
JavaScript 任務運行器 |
| dtslint | TypeScript 類型聲明&樣式校驗工具 |
| TypeScript | 支持TS環(huán)境下開發(fā) |
| Webpack | JavaScript 模塊打包工具 |
| karma | 測試用例檢查器 |
| mocha | 多功能的 JavaScript 測試框架 |
| sinojs | 提供spies, stub, mock,推薦文章《Sinon 入門,看這篇文章就夠了》 |
| follow-redirects | http(s)重定向,NodeJS模塊 |
這里省略了對一些工具介紹,但可以發(fā)現(xiàn),Axios 開發(fā)項目的主功能依賴并不多,換句話說是只有 follow-redirects作為了“使用依賴”,其他都是編譯、測試、框架層面的東西,可以看出官方團隊在對于 Axios 有多么注質(zhì)量和穩(wěn)定性,畢竟是全球都在用的工具。
Axios 中相關代碼都在 lib/ 目錄下(建議逐行閱讀):
.
├── adapters // 網(wǎng)絡請求,NodeJS 環(huán)境使用 NodeJS 的 http 模塊,瀏覽器使用 XHR
│ ├── README.md
│ ├── http.js // Node.js 環(huán)境使用
│ └── xhr.js // 瀏覽器環(huán)境使用
├── helpers // 一些功能輔助工具函數(shù),看文件名可基本知道干啥的
│ ├── README.md
│ ├── bind.js
│ ├── buildURL.js
│ ├── combineURLs.js
│ ├── cookies.js
│ ├── deprecatedMethod.js
│ ├── isAbsoluteURL.js
│ ├── isAxiosError.js
│ ├── isURLSameOrigin.js
│ ├── normalizeHeaderName.js
│ ├── parseHeaders.js
│ ├── spread.js
│ └── validator.js
├── cancel // 取消網(wǎng)絡請求的處理
│ ├── Cancel.js // 取消請求
│ ├── CancelToken.js // 取消 Token
│ └── isCancel.js // 判斷是否取消請求的函數(shù)方法
├── core // 核心功能
│ ├── Axios.js // Axios 對象
│ ├── InterceptorManager.js // 攔截器管理
│ ├── README.md
│ ├── buildFullPath.js // 構造完成的請求 URL
│ ├── createError.js // 創(chuàng)建錯誤,拋出異常
│ ├── dispatchRequest.js // 請求分發(fā),用于區(qū)分調(diào)用 http 還是 xhr
│ ├── enhanceError.js
│ ├── mergeConfig.js // 合并配置參數(shù)
│ ├── settle.js // 根據(jù)請求響應狀態(tài),改變 Promise 狀態(tài)
│ └── transformData.js // 數(shù)據(jù)格式轉(zhuǎn)換
├── env // 無關緊要,沒啥用,與發(fā)包版本有關
│ ├── README.md
│ └── data.js
├── defaults.js // 默認參數(shù)/初始化參數(shù)配置
├── utils.js // 提供簡單的通用的工具函數(shù)
└── axios.js // 入口文件,初始化并導出 axios 對象
有了一個簡單的代碼功能組織架構熟悉后,對于串聯(lián) Axios 的功能很有好處,另外,從上述文件和文件夾的命名,很容易讓人意識到這是一個什么功能的文件。
“高內(nèi)聚、低耦合”的真言,在 Axios 中應該算是一個運用得很好的例子。
二、Axios 網(wǎng)絡請求流程圖
梳理了一張 Axios 發(fā)起請求、響應請求的執(zhí)行流程圖,希望可以給大家一個完整流程的概念,便于理解后續(xù)的源碼分析。

三、Axios API 設計
我們在使用 Axios 的時候,會覺得 Axios 的使用特別方便,其原因就是 Axios 中針對同一功能實現(xiàn)了不同的 API,便于大家在各種場景下的變通擴展使用。
例如,發(fā)起一個 GET 請求的寫法有:
// 第一種
axios('https://xxx.com/api/userInfo?uid=1')
// 第二種
axios.get('https://xxx.com/api/userInfo?uid=1')
// 第三種
axios({
method: 'GET',
url: 'https://xxx.com/api/userInfo?uid=1'
})
Axios 請求的核心方法僅兩種:
axios(config)
// or
axios(url[, config])
我們知道一個網(wǎng)絡請求的方式會有 GET、POST、PUT、DELETE 等,為了使用更加語義化,Axios 對外暴露了別名 API:
axios.request(config)
axios.get(url[, config])
axios.delete(url[, config])
axios.head(url[, config])
axios.options(url[, config])
axios.post(url[, data[, config]])
axios.put(url[, data[, config]])
axios.patch(url[, data[, config]])
通過遍歷擴展axios對象原型鏈上的方法:
// Provide aliases for supported request methods
utils.forEach(['delete', 'get', 'head', 'options'], function forEachMethodNoData(method) {
/*eslint func-names:0*/
Axios.prototype[method] = function(url, config) {
return this.request(mergeConfig(config || {}, {
method: method,
url: url,
data: (config || {}).data
}));
};
});
utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {
/*eslint func-names:0*/
Axios.prototype[method] = function(url, data, config) {
return this.request(mergeConfig(config || {}, {
method: method,
url: url,
data: data
}));
};
});
能夠如上的直接循環(huán)列表賦值,得益于 Axios 將核心的請求功能單獨放到了 Axios.prototype.request 方法中,該方法的 TS 定義為:
Axios.request(config: any, ...args: any[]): any
在其方法(Axios.request())內(nèi)會對外部傳參數(shù)類型做判斷,并選擇組裝正確的請求參數(shù):
// 生成規(guī)范的 config,抹平 API(函數(shù)入?yún)ⅲ┎町?if (typeof config === 'string') {
// 處理了第一個參數(shù)是 url 字符串的情況 request(url[, config])
config = arguments[1] || {};
config.url = arguments[0];
} else {
config = config || {};
}
// 合并默認配置
config = mergeConfig(this.defaults, config);
// 將請求方法轉(zhuǎn)小寫字母,默認為 get 方法
if (config.method) {
config.method = config.method.toLowerCase();
} else if (this.defaults.method) {
config.method = this.defaults.method.toLowerCase();
} else {
config.method = 'get';
}
以此來抹平了各種類型請求以及所需傳入?yún)?shù)之間的差異性!
四、Axios 工廠模式創(chuàng)建實例
默認 Axios 導出了一個單例,導出了一個實例化后的單例,所以我們可以直接引入后就可以調(diào)用 Axios 的方法。
在某些場景下,我們的項目中可能對接了多個業(yè)務方,那么請求中的 base URL 就不一樣,因此有沒有辦法創(chuàng)建多個 Axios 實例?
那就是使用 axios.create([config]) 方法創(chuàng)建多個實例。
考慮到多實例這樣的實際需求,Axios 對外暴露了 create() 方法,在 Axios 內(nèi)部中,往導出的 axios 實例上綁定了用于創(chuàng)建本身實例的工廠方法:
/**
* Create an instance of Axios
*
* @param {Object} defaultConfig The default config for the instance
* @return {Axios} A new instance of Axios
*/
function createInstance(defaultConfig) {
var context = new Axios(defaultConfig);
var instance = bind(Axios.prototype.request, context);
// Copy axios.prototype to instance
utils.extend(instance, Axios.prototype, context);
// Copy context to instance
utils.extend(instance, context);
// Factory for creating new instances
instance.create = function create(instanceConfig) {
return createInstance(mergeConfig(defaultConfig, instanceConfig));
};
return instance;
}
這里的實現(xiàn)值得一說的地方在于:
instance.create = function create(instanceConfig) {
return createInstance(mergeConfig(defaultConfig, instanceConfig));
};
在創(chuàng)建 axios 實例的工廠方法內(nèi),綁定工廠方法到實例的 create 屬性上。為什么不是在工廠方法外綁定吶?這是我們可能的習慣做法,Axios 之前確實也是這么做的。
為什么挪到了內(nèi)部?可以看看這條 PR: Allow axios.create(options) to be used recursively
原因簡單來說就是,用戶自己創(chuàng)建的實例依然可以調(diào)用 create 方法創(chuàng)建新的實例,例如:
const axios = require('axios');
const jsonClient = axios.create({
responseType: 'json' // 該項配置可以在后續(xù)創(chuàng)建的實例中復用,而不必重復編碼
});
const serviceOne = jsonClient.create({
baseURL: 'https://service.one/'
});
const serviceTwo = jsonClient.create({
baseURL: 'https://service.two/'
});
這樣有助于復用實例的公共參數(shù),減少重復編碼。
五、網(wǎng)絡請求適配器
在文件 ./defaults.js 中生成了默認完整的 Request Config 參數(shù)。
其中 config.adapter 字段表明當前應該使用 ./adapters/目錄下的 http.js 還是 xhr.js 模塊
// 根據(jù)當前使用環(huán)境,選擇使用的網(wǎng)絡請求適配器
function getDefaultAdapter() {
var adapter;
if (typeof XMLHttpRequest !== 'undefined') {
// For browsers use XHR adapter
adapter = require('./adapters/xhr');
} else if (typeof process !== 'undefined' && Object.prototype.toString.call(process) === '[object process]') {
// For node use HTTP adapter
adapter = require('./adapters/http');
}
return adapter;
}
這里使用了設計模式中的適配器模式,通過判斷不同環(huán)境下是否支持方法的方式,選擇正確的網(wǎng)絡請求模塊,便可以實現(xiàn)官網(wǎng)所說的支持 NodeJS 和瀏覽器環(huán)境。
六、轉(zhuǎn)換請求體和響應體數(shù)據(jù)
這是 Axios 貼在官網(wǎng)的核心功能之一,且提到了可以自動轉(zhuǎn)換響應體內(nèi)容為 JSON 數(shù)據(jù)


transformRequest 和 transformResponse 字段是一個數(shù)組類型,因此我們還可以向其中增加自定義的轉(zhuǎn)換器。
一般來講我們只會通過復寫 transitional 字段來控制響應數(shù)據(jù)的轉(zhuǎn)換與否,但可以作為擴展 Axios 的一個點,留了口子,這一點考慮得也很到位。
七、請求攔截器&響應攔截器
可以通過攔截器來提前處理請求前和收到響應前的一些處理方法。
7.1 攔截器的使用
攔截器用于在 .then() 和 .catch() 前注入并執(zhí)行的一些方法。
// 通過 use 方法,添加一個請求攔截器
axios.interceptors.request.use(function (config) {
// 在發(fā)送請求前干點啥,.then() 處理之前,比如修改 request config
return config;
}, function (error) {
// 在發(fā)起請求發(fā)生錯誤后,.catch() 處理之前干點啥
return Promise.reject(error);
});
// 通過 use 方法,添加一個響應攔截器
axios.interceptors.response.use(function (response) {
// 只要響應網(wǎng)絡狀態(tài)碼是 2xx 的都會觸發(fā)
// 干點啥
return response;
}, function (error) {
// 狀態(tài)碼不是 2xx 的會觸發(fā)
// 發(fā)生錯誤了,干點啥
return Promise.reject(error);
});
7.2 攔截管理器
Axios 將請求和響應的過程包裝成了 Promise,那么 Axios 是如何實現(xiàn)攔截器在 .then() 和 .catch() 執(zhí)行前執(zhí)行吶?
可以很容易猜到通過組裝一條 Promise 執(zhí)行鏈即可!
來看看 Axios 在請求函數(shù)中如何實現(xiàn):
首先是 Axios 對象中初始化了 攔截管理器:
function Axios(instanceConfig) {
this.defaults = instanceConfig;
this.interceptors = {
request: new InterceptorManager(),
response: new InterceptorManager()
};
}
來到 ./lib/core/InterceptorManager.js 文件下,對于攔截管理器
// 攔截管理器對象
function InterceptorManager() {
this.handlers = [];
}
/**
* 添加新的管理器,定義了 use 方法
*
* @param {Function} fulfilled 處理 `Promise` 執(zhí)行 `then` 的函數(shù)方法
* @param {Function} rejected 處理 `Promise` 執(zhí)行 `reject` 的函數(shù)方法
*
* @return {Number} 返回一個 ID 值用于移除攔截器
*/
InterceptorManager.prototype.use = function use(fulfilled, rejected, options) {
this.handlers.push({
fulfilled: fulfilled,
rejected: rejected,
// 默認不同步
synchronous: options ? options.synchronous : false,
// 定義是否執(zhí)行當前攔截器的函數(shù)或布爾值
runWhen: options ? options.runWhen : null
});
return this.handlers.length - 1; // ID 值實際就是當前攔截器的數(shù)組索引
};
/**
* 從棧中移除指定 id 的攔截器
*
* @param {Number} id use 方法返回的 id 值
*/
InterceptorManager.prototype.eject = function eject(id) {
if (this.handlers[id]) {
this.handlers[id] = null; // 刪除攔截器,但索引會保留
}
};
/**
* 迭代所有注冊的攔截器
* 該方法會跳過因攔截器被刪除而值為 null 的索引
*
* @param {Function} 調(diào)用每個有效攔截器的函數(shù)
*/
InterceptorManager.prototype.forEach = function forEach(fn) {
utils.forEach(this.handlers, function forEachHandler(h) {
if (h !== null) {
fn(h);
}
});
};
迭代所有注冊的攔截器是一個 FIFS(first come first served,先到先服務)隊列執(zhí)行順序的方法。
7.3 組裝攔截器與請求執(zhí)行鏈
在 ./lib/core/Axios.js 文件中,Axios 對象定義了 request 方法,其中將網(wǎng)絡請求、請求攔截器和響應攔截器組裝。
默認返回一個還未執(zhí)行網(wǎng)絡請求的 Promise 執(zhí)行鏈,如果設置了同步,則會立即執(zhí)行請求過程,并返回請求結果的 Promise 對象,也就是官方文檔中提到的 Axios 還支持 Promise API。
函數(shù)詳細的分析,都已經(jīng)注釋在如下代碼中:
/**
* Dispatch a request
*
* @param {Object} config 傳入的用戶自定義配置,并和默認配置 merge
*/
Axios.prototype.request = function request(config) {
// 省略 ...
// 請求攔截器執(zhí)行鏈
var requestInterceptorChain = [];
// 同步請求攔截器
var synchronousRequestInterceptors = true;
// 遍歷請求攔截器
this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
// 判斷 runWhen 如果是函數(shù),則執(zhí)行函數(shù),結果若為 false,則不執(zhí)行當前攔截器
if (typeof interceptor.runWhen === 'function' && interceptor.runWhen(config) === false) {
return;
}
// 判斷當前攔截器是否同步
synchronousRequestInterceptors = synchronousRequestInterceptors && interceptor.synchronous;
// 插入 requestInterceptorChain 數(shù)組首位
// 效果:[interceptor.fulfilled, interceptor.rejected, ...]
requestInterceptorChain.unshift(interceptor.fulfilled, interceptor.rejected);
});
// 響應攔截器執(zhí)行鏈
var responseInterceptorChain = [];
// 遍歷所有的響應攔截器
this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
// 插入 responseInterceptorChain 尾部
// 效果:[ ..., interceptor.fulfilled, interceptor.rejected]
responseInterceptorChain.push(interceptor.fulfilled, interceptor.rejected);
});
var promise;
// 如果非同步
// 一般大家在使用 axios.interceptors.request.use 都沒有傳遞第三個配置參數(shù)
// 所以一般情況下會走這個邏輯
if (!synchronousRequestInterceptors) {
var chain = [dispatchRequest, undefined];
// 將請求攔截器執(zhí)行鏈放到 chain 數(shù)組頭部
Array.prototype.unshift.apply(chain, requestInterceptorChain);
// 將響應攔截器執(zhí)行鏈放到 chain 數(shù)組末尾
chain = chain.concat(responseInterceptorChain);
// 給 promise 賦值 Promise 對象,并注入 request config
promise = Promise.resolve(config);
// 循環(huán) chain 數(shù)組,組合成 Promise 執(zhí)行鏈
while (chain.length) {
// 正好 resolve 和 reject 對應方法,兩兩一組
promise = promise.then(chain.shift(), chain.shift());
}
// 返回 Promise 執(zhí)行鏈
return promise;
}
// 同步方式
var newConfig = config;
// 循環(huán)并執(zhí)行所有請求攔截器
while (requestInterceptorChain.length) {
var onFulfilled = requestInterceptorChain.shift();
var onRejected = requestInterceptorChain.shift();
try {
// 執(zhí)行定義請求前的“請求攔截器” then 處理方法
newConfig = onFulfilled(newConfig);
} catch (error) {
// 執(zhí)行定義請求前的“請求攔截器” catch 處理方法
onRejected(error);
break;
}
}
try {
// 執(zhí)行網(wǎng)絡請求
promise = dispatchRequest(newConfig);
} catch (error) {
return Promise.reject(error);
}
// 循環(huán)并執(zhí)行所有響應攔截器
while (responseInterceptorChain.length) {
promise = promise.then(responseInterceptorChain.shift(), responseInterceptorChain.shift());
}
// 返回 Promise 對象
return promise;
};
可以看到由于請求攔截器和響應攔截器使用了 unshift 和 push,那么 use 攔截器的先后順序就有變動。
通過如上代碼的分析,可以得知若有多個攔截器的執(zhí)行順序規(guī)則是:
- 請求攔截器:先 use,后執(zhí)行
- 響應攔截器:先 use,先執(zhí)行
關于攔截器執(zhí)行這部分,涉及到一個 PR改動: Requests unexpectedly delayed due to an axios internal promise,推薦大家閱讀一下,有助于熟悉微任務和宏任務。
改動的原因:如果請求攔截器中存在一些長時間的任務,會使得使用 axios 的網(wǎng)絡請相較于不使用 axios 的網(wǎng)絡請求會延后,為此,通過為攔截管理器增加 synchronous 和 runWhen 字段,來實現(xiàn)同步執(zhí)行請求方法。
八、取消網(wǎng)絡請求
在網(wǎng)絡請求中,會遇到許多非預期的請求取消,當然也有主動取消請求的時候,例如,用戶獲取 id=1 的新聞數(shù)據(jù),需要耗時 30s,用戶等不及了,就返回查看 id=2 的新聞詳情,此時我們可以在代碼中主動取消 id=1 的網(wǎng)絡請求,節(jié)省網(wǎng)絡資源。
8.1 如何取消 Axios 請求
通過 CancleToken.source() 工廠方法創(chuàng)建取消請求的實例 source
在發(fā)起請求的 request Config 中設置 cancelToken 值為 source.token
在需要主動取消請求的地方調(diào)用:source.cancle()
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
})
// 主動取消請求 (提示信息是可選的參數(shù))
source.cancel('Operation canceled by the user.');
同一個 source 實例調(diào)用取消 cancle() 方法時,會取消所有含有當前實例 source.token 的請求
8.2 取消請求功能的原理
想必大家也很好奇是怎么實現(xiàn)取消網(wǎng)絡請求功能的,實際上有了上述的基礎,把 Axios 的請求想象成為一條事件執(zhí)行鏈,執(zhí)行鏈中任意一處發(fā)生了異常,都會中斷整個請求。
整個請求執(zhí)行鏈中的設計了,首先來看:axios.CancelToken.source():
/**
* Returns an object that contains a new `CancelToken` and a function that, when called,
* cancels the `CancelToken`.
*/
CancelToken.source = function source() {
var cancel;
var token = new CancelToken(function executor(c) {
cancel = c;
});
return {
token: token,
cancel: cancel
};
};
該工廠方法返回了一個對象,該對象包含了一個 token(取消令牌,CancleToken 對象的實例),以及一個取消與 token 映射綁定的取消請求方法 cancle()
其中 new CancelToken() 會創(chuàng)建 CancleToken 的單例,通過傳入函數(shù)方式,拿到了取消請求的回調(diào)函數(shù),該函數(shù)內(nèi)會構造 token 取消的原因,并通過執(zhí)行 resolvePromise(),主動 reslove。
同樣是一個微任務,當主動調(diào)用 cancle() 方法后,會調(diào)用 resolvePromise(reason),此時就會給當前 cancleToken 實例的 reason 字段賦值“請求取消的原因”:
function CancelToken(executor) {
if (typeof executor !== 'function') {
throw new TypeError('executor must be a function.');
}
// 初始化一個 promise 屬性,resolvePromise 變量指向 resolve
var resolvePromise;
this.promise = new Promise(function promiseExecutor(resolve) {
resolvePromise = resolve;
});
// 賦值 token 為當前對象的實例
var token = this;
// 省略...
// 執(zhí)行外部傳入的初始化方法,將取消請求的方法,賦值給返回對象的 cancel 屬性
executor(function cancel(message) {
if (token.reason) {
// Cancellation has already been requested
return;
}
token.reason = new Cancel(message);
resolvePromise(token.reason);
});
}
在 ./lib/core/dispatchRequest.js 文件中:
function throwIfCancellationRequested(config) {
// 當 request config 中有實例化 cancelToken 時
// 執(zhí)行 throwIfRequested() 方法
// throwIfRequested() 方法在 cancleToken 實例的 reason 字段有值時
// 拋出異常
if (config.cancelToken) {
config.cancelToken.throwIfRequested();
}
// 判斷 config.signal.aborted 值為真的時候拋出異常
// 該值時通過 new AbortController().signal,不過目前暫時未用到
// 官方文檔上暫也暫未更新相關內(nèi)容
if (config.signal && config.signal.aborted) {
throw new Cancel('canceled');
}
}
module.exports = function dispatchRequest(config) {
// 準備發(fā)起請求前檢查
throwIfCancellationRequested(config);
// 省略...
var adapter = config.adapter || defaults.adapter;
return adapter(config).then(function onAdapterResolution(response) {
// 請求成功后檢查
throwIfCancellationRequested(config);
// 省略...
return response;
}, function onAdapterRejection(reason) {
if (!isCancel(reason)) {
// 請求發(fā)生錯誤時候檢查
throwIfCancellationRequested(config);
// 省略...
}
// 省略...
return Promise.reject(reason);
});
}
在文章前邊分析攔截器的時候講到了 dispatchRequest() 在請求攔截器之后執(zhí)行。
在請求前,請求成功、失敗后三個時機點,都會通過 throwIfCancellationRequested() 函數(shù)檢查是否取消了請求,throwIfCancellationRequested() 函數(shù)判斷了 cancleToken.reason 是否有值,如果有則拋出異常并中斷請求 Promise 執(zhí)行鏈。
九、CSRF 防御
Axios 支持防御 CSRF(Cross-site request forgery,跨站請求偽造)攻擊,而防御 CSRF 攻擊的最簡單方式就是加 Token。
CSRF 的攻擊可以簡述為:服務器錯把攻擊者的請求當成了正常用戶的請求。
加一個 Token 為什么就能解決吶?首先 Token 是服務端隨用戶每次請求動態(tài)生成下發(fā)的,用戶在提交表單、查詢數(shù)據(jù)等行為的時候,需要在網(wǎng)絡請求體加上這個臨時性的 Token 值,攻擊者無法在三方網(wǎng)站中獲取當前 Token,因此服務端就可以通過驗證 Token 來區(qū)分是否是正常用戶的請求。
Axios 在請求配置中提供了兩個字段:
// cookie 中攜帶的 Token 名稱,通過該名稱可以從 cookie 中拿到 Token 值
xsrfCookieName: 'XSRF-TOKEN',
// 請求 Header 中攜帶的 Token 名稱,通過該成名可從 Header 中拿到 Token 值
xsrfHeaderName: 'X-XSRF-TOKEN',
用于附加驗證防御 CSRF 攻擊的 Token。
十、值得一說的自定義工具庫
在 Axios 內(nèi),沒有引入其他例如 lodash 的工具函數(shù)依賴,都在自己內(nèi)部按需實現(xiàn)了工具函數(shù),提供給整個項目使用。
個人非常喜歡這種做法,尤其是在一個 ES5 的工具庫下,這樣做不僅代碼易讀,與此同時還顯得非常得純粹、干凈、清晰!
如果團隊內(nèi)有這種訴求,建議可以寫一個 ESM 模塊的工具庫,這樣做以后,在打包 Tree Shaking 時,打包的結果應該能更加干凈。
總結
總體來說,Axios 涉及到的設計模式就有:單例模式、工廠模式、職責鏈模式、適配器模式,因此絕對是值得學習的一個工具庫,梳理之后不僅利于我們靈活使用其 API,更有助于根據(jù)業(yè)務去自定義擴展封裝網(wǎng)絡請求,將網(wǎng)絡請求統(tǒng)一收口。
與此同時,Axios 絕對是一個可以作為軟件工程編碼的學習范本,其中的文件夾結構,功能設計,功能解耦,按需封裝工具類,以及靈活運用設計模式都是值得揣度回味。
相信能看到文末的你,一定收獲不小,不妨動動小手,點個關注?
