業(yè)務(wù)需求
公司有多個(gè)項(xiàng)目共同用到了已封裝好的axios(funcation http()),基于去掉復(fù)制粘貼這種重復(fù)工作。
- 統(tǒng)一封裝所有項(xiàng)目axios方法(
)
- 把封裝好的方法打包發(fā)布到公司npm私服(不涉及)
問題重現(xiàn)
Vue項(xiàng)目頁面Create(){}的時(shí)候調(diào)用了一個(gè)接口,相當(dāng)于調(diào)用了http()方法,此時(shí)響應(yīng)response還是正常的。
但是當(dāng)再調(diào)用任何接口的時(shí)候,response出現(xiàn)了多次(我在響應(yīng)攔截器中console了response,調(diào)用了幾次http(),就多出現(xiàn)幾次console)
// axios 封裝的方法(關(guān)鍵代碼)
// httpClient是axios重命名、因?yàn)槊總€(gè)項(xiàng)目的baseURL不一樣,所以每次需要傳一個(gè)base進(jìn)來、url是接口地址
export default function http(base, url, data = {}, type = "get", header = {}) {
httpClient.defaults.baseURL = base;
httpClient.interceptors.response.use(
response => {
console.log("response", response);
return response
},
error => {
console.log("error", error.response);
if (error.response.status === 401 && error.response.data.code === 'nosso') {
window.location.href = base + '/redirect/to/frontend?page=' + encodeURIComponent(window.location.href)
}
return error.response
}
);
type = type.toLowerCase();
let promise;
return new Promise((resolve, reject) => {
if (type === 'post') {
promise = httpClient.post(url, data, {headers: header})
} else if (type === 'put') {
promise = httpClient.put(url, data, {headers: header})
} else if (type === 'delete') {
promise = httpClient.delete(url, {data: data, headers: header})
} else {
promise = httpClient.get(url, {params: data, headers: header})
}
promise.then(response => {
resolve(response.data);
}).catch(error => {
reject(error)
})
})
}



如果再點(diǎn)退出,response的數(shù)量會(huì)繼續(xù)累加。當(dāng)時(shí)看到這個(gè)情況后很不理解。首先能確定的是兩個(gè)請(qǐng)求格式和參數(shù)完全一致,那么問題只能在響應(yīng)和后端數(shù)據(jù)上了(排除后臺(tái)數(shù)據(jù)問題)。
百度了一圈,基本沒有發(fā)現(xiàn)這個(gè)問題的解決辦法(本來是沒有問題的,這個(gè)封裝的方法最后是由我自己改動(dòng)的。詳細(xì)改動(dòng):將baseURL和響應(yīng)攔截器放在了http()里面,就出現(xiàn)以上錯(cuò)誤。之所以會(huì)放在里面是因?yàn)閎ase位置問題,后面細(xì)說)。
為什么會(huì)出現(xiàn)這個(gè)問題 && 問題原因
項(xiàng)目以前http()的響應(yīng)攔截器里的地址是寫死的,封裝后,需要根據(jù)不同的項(xiàng)目給不同的請(qǐng)求前綴地址。而axios默認(rèn)的請(qǐng)求配置是在發(fā)生請(qǐng)求之前定義的。

如圖,調(diào)用http()的時(shí)候,需要將一個(gè)base傳入baseURL和響應(yīng)攔截器里面。因?yàn)槭桥渲庙?xiàng),有點(diǎn)違背代碼邏輯,所以將baseURL和響應(yīng)攔截器放在http()里面,如下圖:

回到上面的問題,我猜測把響應(yīng)攔截器放在http()后,因?yàn)槊看味加袀€(gè)new Promise的過程,相當(dāng)于在項(xiàng)目里,每調(diào)一次http(),axios就多一個(gè)響應(yīng)攔截器配置,每個(gè)響應(yīng)都依次依賴,所以才導(dǎo)致調(diào)用一個(gè)接口,就多一個(gè)response結(jié)果。
解決
找到問題就好解決,最開始沒有想到,httpClient.default.baseURL可以當(dāng)做axios服務(wù)的全局變量。也就是說,我只需要把這個(gè)default.baseURL放在http()里面,響應(yīng)攔截器放在外面就可以了
httpClient.interceptors.response.use(
response => {
return response.data
},
error => {
if (error.response.status === 401 && error.response.data.code === 'nosso') {
window.location.href = httpClient.defaults.baseURL + '/redirect/to/frontend?page=' + encodeURIComponent(window.location.href)
}
return error.response
}
);
/**
* 封裝請(qǐng)求方法
* @params(url)
* @params(data)
* returns { Promise }
* @user NIGangJun <nigangjun@aeotrade.com>
*/
export default function http(base, url, data = {}, type = "get", header = {}) {
httpClient.defaults.baseURL = base;
type = type.toLowerCase();
let promise;
return new Promise((resolve, reject) => {
if (type === 'post') {
promise = httpClient.post(url, data, {headers: header})
} else if (type === 'put') {
promise = httpClient.put(url, data, {headers: header})
} else if (type === 'delete') {
promise = httpClient.delete(url, {data: data, headers: header})
} else {
promise = httpClient.get(url, {params: data, headers: header})
}
promise.then(response => {
resolve(response);
})
})
}
就是把響應(yīng)攔截器放在外面,然后base替換成baseURL就行了。
有個(gè)優(yōu)化的地方:響應(yīng)攔截器resolve的數(shù)據(jù)換成了response.data,reject的數(shù)據(jù)換成了error.response。promise就直接去掉了catch(如果要catch,響應(yīng)攔截器中reject的數(shù)據(jù)要return Promise.reject(error.response),并且http()方法里不能再使用 new Promise封裝)
總結(jié)
雖然最后解決結(jié)果很簡單,但是中間這個(gè)過程不可忽略。
- axios配置應(yīng)該符合規(guī)范;
- 查找代碼問題應(yīng)該按照功能進(jìn)行拆分,依次查詢;