axios對(duì)外出口API的設(shè)計(jì)方法
本文我們將討論 axios 對(duì)外出口 API 是如何設(shè)計(jì)的。
axios 的 2 種使用方式
當(dāng)通過 npm install axios 安裝完 axios 之后,就可以以下 2 種方式使用 axios。
一種是在請(qǐng)求時(shí)傳入 axios 配置項(xiàng)。
// GET 請(qǐng)求
axios.get('/user', {
params: {
ID: 12345
}
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
})
.finally(function () {
// always executed
});
// POST 請(qǐng)求
axios.post('/user', {
firstName: 'Fred',
lastName: 'Flintstone'
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
還有一種,就是通過 axios.create() 創(chuàng)建一個(gè)包含預(yù)配置的 axios,再進(jìn)行請(qǐng)求。
const instance = axios.create({
baseURL: 'https://some-domain.com/api/',
timeout: 1000,
headers: {'X-Custom-Header': 'foobar'}
});
axios.get('/user')
axios.post('/user', {firstName: 'Fred',lastName: 'Flintstone'})
這樣就避免了每個(gè)請(qǐng)求中總是重復(fù)書寫相同配置的苦惱。
請(qǐng)求方法別名
在實(shí)際項(xiàng)目中使用 axios 請(qǐng)求時(shí),我們通過會(huì)使用 axios.method() 的方式發(fā)送請(qǐng)求。這樣的方法一共有 8 個(gè):
- 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(config)
這 8 個(gè)請(qǐng)求方法,底層都是基于 axios.request(config) 封裝的。
其中:
- axios(config) 是 axios.request(config) 的別名
- axios.get、axios.delete、axios.head、axios.options(url[, config]) 類似于 axios.request({ ...config, method, url, data: config?.data })
- axios.post、axios.put、axios.patch(url[, data[, config]]) 類似于 axios.request({ ...config, method, url, data })
了解了這些請(qǐng)求方法別名后,我們就來看它們的底層實(shí)現(xiàn)。
Axios 類
axios 其實(shí)是內(nèi)部 Axios 類的實(shí)例。Axios 類的源碼位于 lib/core/Axios.js。
其總體實(shí)現(xiàn)如下:
// /v1.6.8/lib/core/Axios.js
class Axios {
// 1)
constructor(instanceConfig) {
this.defaults = instanceConfig;
this.interceptors = {
request: new InterceptorManager(),
response: new InterceptorManager()
};
}
// 2)
async request(configOrUrl, config) {/* ... */}
// 3)
getUri(config) {/* ... */}
}
// 3)
['delete', 'get', 'head', 'options'].forEach(function forEachMethodNoData(method) {/* ... */})
['post', 'put', 'patch'].forEach(function forEachMethodNoData(method) {/* ... */})
1):可以看到,Axios 類的實(shí)現(xiàn)代碼還是比較少的,就 2 個(gè)方法:
async request()就對(duì)應(yīng)前面所說的 axios.request(),是所有請(qǐng)求的入口地方getUri()比較少用,是用來獲得完整請(qǐng)求路徑(基于當(dāng)前 axios 的默認(rèn)配置和你傳入的配置)
2):另外,在創(chuàng)建 Axios 實(shí)例時(shí),實(shí)例對(duì)象上會(huì)綁定 defaults、interceptors 屬性。
- defaults 是在創(chuàng)建 Axios 實(shí)例傳入的配置,作為默認(rèn)配置用
- interceptors 則是攔截器配置入口,如果你設(shè)置過攔截器,肯定知道它
3):最后這部分的代碼,其實(shí)就是基于 Axios.prototype.request 方法進(jìn)行封裝,分別在 Axios.prototype 上添加 'delete'、'get'、'head'、'options' 和 'post'、'put'、'patch' 方法,前 4 個(gè)是一類,后 3 個(gè)是一類。
接下來,我們?cè)敿?xì)說一下。
Axios.prototype.request()
這是 axios 實(shí)現(xiàn)核心請(qǐng)求邏輯的方法。
為了避免贅述,我這里給出偽代碼實(shí)現(xiàn)。
async request(configOrUrl, config) {
// 獲得傳入的配置
config = getFinalConfigBy(configOrUrl, config)
// 與內(nèi)部默認(rèn)配置合并
config = mergeConfig(this.defaults, config);
// 拼裝完整請(qǐng)求鏈
const chain = []
.concat(requestInterceptorChain)
.concat(dispatchRequest)
.concat(responseInterceptorChain)
// 發(fā)起并返回請(qǐng)求結(jié)果
return request(chain)
}
axios.defaults/interceptors
這是 Axios 實(shí)例上的兩個(gè)屬性。
axios.defaults 允許你做默認(rèn)配置的修改,會(huì)對(duì)所有當(dāng)前 axios 實(shí)例的請(qǐng)求都生效。
axios.interceptors 則用來提供配置請(qǐng)求/響應(yīng)攔截器。interceptors.request 允許你在請(qǐng)求前修改請(qǐng)求配置,interceptors.response 則允許你在請(qǐng)求得到響應(yīng)后、交由用戶處理前,提前統(tǒng)一對(duì)響應(yīng)數(shù)據(jù)做處理。
request、response 2 個(gè)屬性都是 InterceptorManager 實(shí)例,提供 use()/eject()/clear()(對(duì)外) 方法和 forEach()(對(duì)內(nèi))方法。
有興趣的想要了解攔截器實(shí)現(xiàn)機(jī)制的讀者,可以瀏覽之前的《axios 攔截器機(jī)制是如何實(shí)現(xiàn)的?》 一文進(jìn)行學(xué)習(xí)。
請(qǐng)求方法別名
再來看看請(qǐng)求方法別名的實(shí)現(xiàn)。
先看 'delete'、'get'、'head'、'options' 這 4 個(gè)方法。
// /v1.6.8/lib/core/Axios.js#L193-L202
['delete', 'get', 'head', 'options'].forEach(function forEachMethodNoData(method) {
Axios.prototype[method] = function(url, config) {
return this.request(mergeConfig(config || {}, {
method,
url,
data: (config || {}).data
}));
};
});
實(shí)現(xiàn)稍稍簡(jiǎn)單。本質(zhì)上就是為 Axios.prototype 分別添加以上述 4 種請(qǐng)求方式為名的請(qǐng)求實(shí)現(xiàn)。this.request() 就是 Axios.prototype.request() 方法。
以 axios.delete(url, config) 為例:'delete' 作為 config.method,url 作為 config.url,最后和傳入的 config 合并成一個(gè),交由 axios.request() 處理。
再來看看 'post'、'put'、'patch' 這 3 個(gè)方法的實(shí)現(xiàn)。
// /v1.6.8/lib/core/Axios.js#L204-L223
['post', 'put', 'patch'].forEach(function forEachMethodWithData(method) {
function generateHTTPMethod(isForm) {
return function httpMethod(url, data, config) {
return this.request(mergeConfig(config || {}, {
method,
headers: isForm ? {
'Content-Type': 'multipart/form-data'
} : {},
url,
data
}));
};
}
Axios.prototype[method] = generateHTTPMethod();
Axios.prototype[method + 'Form'] = generateHTTPMethod(true);
});
會(huì)稍稍復(fù)雜一丟丟。因?yàn)?axios 還額外支持了 'postForm'、'putForm'、'patchForm' 3 個(gè)方法,用于傳輸文件的場(chǎng)景。
以 axios.post(url, data, config) 為例:'post' 作為 config.method,url 作為 config.url,data 作為 config.data,最后和傳入的 config 合并成一個(gè),交由 axios.request() 處理。
以 axios.postForm(url, data, config) 為例,相比 axios.post(url, data, config),多傳入了一個(gè) 'Content-Type': 'multipart/form-data' 的請(qǐng)求頭配置。這個(gè)請(qǐng)求頭項(xiàng)會(huì)幫助后端理解要處理的請(qǐng)求類型。
導(dǎo)出 axios
以上我們介紹了 Axios 類的邏輯實(shí)現(xiàn)。
如果直接導(dǎo)出 Axios
不過,如果只導(dǎo)出 Axios 類作為對(duì)外輸出,那么使用方式就是下面這樣。
//////
// axios.js
//////
export { default as defaults } from './defaults/index.js';
export default Axios;
//////
// app.js
//////
import Axios, { defaults } from 'axios'
// 使用方式一
new Axios(defaults).get('/user')
new Axios(defaults).post('/user', {firstName: 'Fred',lastName: 'Flintstone'})
// 使用方式二
const createAxios = () {
return new Axios(defaults)
}
const axios = createAxios()
axios.get('/user')
axios.post('/user', {firstName: 'Fred',lastName: 'Flintstone'})
new Axios() 創(chuàng)建實(shí)例的方式不夠優(yōu)雅,而且每次使用都會(huì)有一段樣板代碼。
為了減少這部分的工作,axios 團(tuán)隊(duì)在導(dǎo)出時(shí)針對(duì) Axios 做了一層封裝,讓導(dǎo)出 API 更加好用。源碼位于 lib/axios.js。
// /v1.6.8/lib/axios.js#L28-L44
function createInstance(defaultConfig) {
// 1.1)
const context = new Axios(defaultConfig);
const instance = Axios.prototype.request.bind(context);
// 2)
// Copy axios.prototype to instance
utils.extend(instance, Axios.prototype, context, {allOwnKeys: true});
// Copy context to instance
utils.extend(instance, context, null, {allOwnKeys: true});
// 3)
// Factory for creating new instances
instance.create = function create(instanceConfig) {
return createInstance(mergeConfig(defaultConfig, instanceConfig));
};
// 1.2)
return instance;
}
// 1)
// Create the default instance to be exported
const axios = createInstance(defaults);
// this module should only have a default export
export default axios
1)、首先,導(dǎo)出的 axios 其實(shí)是 createInstance(defaults) 的返回值
參數(shù) defaults 是 axios 內(nèi)部默認(rèn)配置信息。
import defaults from './defaults/index.js';
而 createInstance() 內(nèi)部,返回的其實(shí)并不是 Axios 實(shí)例,而是 Axios 實(shí)例的 request() 方法。
function createInstance(defaultConfig) {
// 返回的并不是 Axios 實(shí)例
const context = new Axios(defaultConfig);
// 而是 Axios 實(shí)例的 request() 方法
const instance = Axios.prototype.request.bind(context);
// ...
return instance;
}
這樣,我們就能以 axios(config) 方式發(fā)起請(qǐng)求,沒必要寫成 axios.request(config) 這樣了。
同時(shí)值得注意的時(shí),request() 方法內(nèi)部的 this 綁定到了 Axios 實(shí)例(即 context)上。這樣,request() 方法內(nèi)部訪問的 this 時(shí)就不會(huì)有問題了。
2)、不過 instance(也就是 axios.request)上現(xiàn)在是沒有 Axios 實(shí)例上的其他方法了!
因此,接下來我們把 Axios.prototype 和 context 上的屬性都復(fù)制給 instance。
function createInstance(defaultConfig) {
const context = new Axios(defaultConfig);
const instance = Axios.prototype.request.bind(context);
// 把 Axios.prototype 上的屬性都復(fù)制給 instance
utils.extend(instance, Axios.prototype, context, {allOwnKeys: true});
// 把 context 上的屬性都復(fù)制給 instance
utils.extend(instance, context, null, {allOwnKeys: true});
// ...
return instance;
}
如此一來,我們就能在 instance 上調(diào)用 get()/post()/getUri() 等這些方法了。
3)另外,在來實(shí)現(xiàn) axios.create()
function createInstance(defaultConfig) {
// ...
// 基于內(nèi)部默認(rèn)配置,在創(chuàng)建一個(gè)新的 Axios 實(shí)例
instance.create = function create(instanceConfig) {
return createInstance(mergeConfig(defaultConfig, instanceConfig));
};
return instance;
}
可以看到 instance.create 手動(dòng)增加了 create() 方法。
create() 方法內(nèi)部其實(shí)就是遞歸調(diào)用 createInstance()。不同的是,instance.create() 本質(zhì)上是基于導(dǎo)出的 axios 的基礎(chǔ)上再多一步自定義配置合并。
總結(jié)
本文我們講解了 axios 對(duì)外出口 API 是如何設(shè)計(jì)的。
我們首先介紹了內(nèi)部 Axios 類的實(shí)現(xiàn),這是 axios 核心邏輯所在;其次為了讓出口 API 更好使用,真正導(dǎo)出的 axios 其實(shí)是 Axios 實(shí)例的 request() 方法,最后又在增加了 create() 方法,方便進(jìn)一步得到擁有自定義配置的 axios 對(duì)象。
以上就是axios對(duì)外出口API的設(shè)計(jì)方法的詳細(xì)內(nèi)容,更多關(guān)于axios對(duì)外出口API的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
javascript 制作坦克大戰(zhàn)游戲初步 圖片與代碼
javascript 制作坦克大戰(zhàn)游戲初步 圖片與代碼...2007-11-11
用javascript實(shí)現(xiàn)li 列表數(shù)據(jù)隔行變換背景顏色
客戶端效果,效率自然不錯(cuò)。以前的做法是偶數(shù)行時(shí)給li加一個(gè)class,方法當(dāng)然不可取,如果后臺(tái)讀取再加class就很麻煩了,看看這個(gè)效果2007-08-08
微信小程序數(shù)據(jù)劫持代理的實(shí)現(xiàn)
本文主要介紹了微信小程序?數(shù)據(jù)劫持代理的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-01-01
JavaScript canvas實(shí)現(xiàn)動(dòng)態(tài)點(diǎn)線效果
這篇文章主要為大家詳細(xì)介紹了JavaScript canvas實(shí)現(xiàn)動(dòng)態(tài)點(diǎn)線效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-08-08
JavaScript中三個(gè)等號(hào)和兩個(gè)等號(hào)的區(qū)別(== 和 ===)淺析
javascript中比較運(yùn)算符'=='與'==='可能大家用的比較多,但是大家對(duì)他的區(qū)別不是很清楚,接下來小編給大家介紹下js中三個(gè)等號(hào)和兩個(gè)等號(hào)的區(qū)別(== 和 ===),感興趣的朋友可以參考下2016-09-09
uni-app動(dòng)態(tài)修改導(dǎo)航欄標(biāo)題簡(jiǎn)單步驟
uniapp作為一款開源軟件,可以做到一端多用,不過也有局限,在開發(fā)中有時(shí)候需要?jiǎng)討B(tài)的去修改標(biāo)題,下面這篇文章主要給大家介紹了關(guān)于uni-app動(dòng)態(tài)修改導(dǎo)航欄標(biāo)題的相關(guān)資料,需要的朋友可以參考下2023-06-06
layui的布局和表格的渲染以及動(dòng)態(tài)生成表格的方法
今天小編就為大家分享一篇layui的布局和表格的渲染以及動(dòng)態(tài)生成表格的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2019-09-09

