1. 前言
hello,大家好,終于改完
bug了?,F(xiàn)在就開始來(lái)填坑了。在上一篇文章如何優(yōu)雅的使用Vuex中提到了使用axios插件封裝的一個(gè)統(tǒng)一的api庫(kù)。這篇文章就告訴大家如何封裝一個(gè)高效且簡(jiǎn)易使用的api接口庫(kù)。
2. 為什么要封裝axios
剛回武漢的時(shí)候,接手一個(gè)新的項(xiàng)目,剛好這個(gè)項(xiàng)目也是用的axios與后臺(tái)進(jìn)行及交互。具體的使用如下:
this.$axios.post(
"/flow/getSchedule",
this.$qs.stringify({
userId: sessionStorage.getItem("userId"),
pageNumber: pageNumber,
pageSize: 4
}))
.then(resp => {
this.tableData = resp.data.data;
if (resp.data.data && resp.data.data.totalRow) {
sessionStorage.setItem("scheduleCount", resp.data.data.totalRow);
}
});
當(dāng)然這種使用方法是再與后臺(tái)進(jìn)行交互方面是沒(méi)有什么問(wèn)題,但是在接口如果繁多并且不止再一個(gè)路由中使用時(shí),一旦涉及到接口參數(shù)或者接口url地址的修改就會(huì)讓人很崩潰。并且也沒(méi)做統(tǒng)一的錯(cuò)誤返回處理。前前后后接手的人也很多,所以就出現(xiàn)有的錯(cuò)誤是彈窗提示,有的錯(cuò)誤是頁(yè)面上方的message。有的就干脆不給提示。
我當(dāng)時(shí)也有問(wèn)交接的同事為啥不做一下封裝,誰(shuí)知道這位年輕人
啪的一下就站起來(lái),很快啊,上來(lái)就是一個(gè)不知道,翻手一個(gè)沒(méi)必要。我大意了,還沒(méi)來(lái)得及反駁,他人就溜了。為了后面自己不再踩坑,我就開始著手進(jìn)行接口封裝。
3. 如何封裝axios
我這篇文章主要講解如何封裝axios,前提是你已經(jīng)熟悉了如何在v-cli腳手架中使用axios。還不了解的小伙伴可以移步axios官方文檔中學(xué)習(xí)axios的具體使用。
3-1. 修改基礎(chǔ)配置
- 首先我們通過(guò)
npm install axios命令下載axios插件 - 然后在
V-cli項(xiàng)目的main.js中引入
import axios from "axios";
Vue.prototype.$axios = axios;
- 因?yàn)槲覀円诹硗獾?code>js使用到
$axios,所以這里要對(duì)main.js做一下修改,讓我們方便在其他非vue的文件中使用到Vue的實(shí)例。
export const app = new Vue({
router,
store,
render: h => h(App)
}).$mount("#app");
- 在其他文件中使用
// 這里的app就是Vue的實(shí)例了。
import { app } from "../main";
3.2. 創(chuàng)建api.js接口庫(kù)
3.2-1. 我們先在src文件夾下新增utils文件夾,新建api.js文件,編輯該文件
// 引入Vue實(shí)例對(duì)象
import { app } from "../main";
// 設(shè)置請(qǐng)求次數(shù)
let seq = 0;
// 設(shè)置基礎(chǔ)的統(tǒng)一接口方法
function axiosFn(options) {
const config = {
// 設(shè)置請(qǐng)求頭和默認(rèn)請(qǐng)求方式
// 默認(rèn)為get請(qǐng)求
method: "get",
// 設(shè)置請(qǐng)求頭方式
headers: {
"Content-Type": "application/json;charset=utf-8",
}
};
// 這里是對(duì)傳入的基礎(chǔ)配置進(jìn)行處理
for (const k in options) {
if (k !== "param" && options.hasOwnProperty(k)) {
config[k] = options[k];
}
}
// 設(shè)置不同請(qǐng)求的參數(shù)
.....
}
第一步主要是設(shè)置基礎(chǔ)的配置,包括
請(qǐng)求頭和請(qǐng)求方式的設(shè)置。
3.2-2. 根據(jù)請(qǐng)求方式和請(qǐng)求頭的不同設(shè)置不同傳參方式
// 設(shè)置不同請(qǐng)求的參數(shù)
if (
config.method === "post"
|| config.method === "put"
|| config.method === "delete"
) {
// 處理formData數(shù)據(jù)上傳
if (config.headers["Content-Type"] === "multipart/form-data") {
let formData = new FormData();
Object.keys(options.param).forEach(key => {
formData.append(key, options.param[key]);
});
config["data"] = formData;
}
// 請(qǐng)求頭為application/json的請(qǐng)求參數(shù)傳遞
if (config.headers["Content-Type"] === "application/json;charset=utf-8") {
config["data"] = options.param;
}
// 請(qǐng)求頭為application/x-www-form-urlencoded的請(qǐng)求參數(shù)傳遞
if (config.headers["Content-Type"] === "application/x-www-form-urlencoded") {
let postData = "";
for (const key in options.param) {
// 設(shè)置傳參為數(shù)組時(shí)使用jsonStringfy方法轉(zhuǎn)字符串
if (
Object.prototype.toString.apply(options.param[key])
=== "[object Array]"
|| Object.prototype.toString.apply(options.param[key])
=== "[object Object]"
) {
options.param[key] = JSON.stringify(options.param[key]);
}
postData
+= encodeURIComponent(key)
+ "="
+ encodeURIComponent(options.param[key])
+ "&";
}
config["data"] = postData;
}
} else if (config.method === "get") {
// 有可能是字符串這里暫時(shí)先寫非全等
if (options["param"] != undefined || options["param"] != null) {
Object.keys(options["param"]).forEach(v => {
if (
Object.prototype.toString.apply(options["param"][v])
=== "[object Array]"
|| Object.prototype.toString.apply(options.param[v])
=== "[object Object]"
) {
options["param"][v] = JSON.stringify(options["param"][v]);
}
});
}
config["params"] = options["param"];
}
// 攔截請(qǐng)求結(jié)果
.....
注意:這里的代碼本來(lái)是沒(méi)有這么復(fù)雜的,最開始只是設(shè)置最基礎(chǔ)的get,post請(qǐng)求參數(shù)。后來(lái)因?yàn)檫@個(gè)api庫(kù)被他們拿去用到各個(gè)項(xiàng)目中。出現(xiàn)了一大堆的問(wèn)題。但是他們自己又不會(huì)針對(duì)項(xiàng)目作出對(duì)應(yīng)的修改。一出問(wèn)題就噴我封裝的不對(duì),難用。我又不能和他意氣用事。只有一張圖表達(dá)我的心情。

咳咳,回到上述代碼中,這里
get請(qǐng)求涉及到的處理不多,主要是其他的請(qǐng)求和請(qǐng)求頭的不同的處理
-
get請(qǐng)求處理:主要是對(duì)params參數(shù)中數(shù)組和對(duì)象進(jìn)行JSON.stringify轉(zhuǎn)碼就好了。這個(gè)是我們這邊的與后臺(tái)約定的統(tǒng)一處理方法。大家如果沒(méi)有這方面的需求可以不加這層判斷。 -
post/put/delete請(qǐng)求處理:這里主要是對(duì)不同的請(qǐng)求頭進(jìn)行處理。典型的處理是針對(duì)上傳文件的處理。通過(guò)這層處理后,我們?cè)诼?lián)調(diào)上傳接口時(shí),就不用使用new FormData()去包裝我們的上傳參數(shù)了。其它兩種也是常用的請(qǐng)求頭判斷和處理。這個(gè)就看具體的使用項(xiàng)目了。
3.2-3. 設(shè)置axios攔截器
// 攔截請(qǐng)求結(jié)果
app.$axios.interceptors.response.use(
function(response) {
response.seq = seq;
// 請(qǐng)求成功返回response
if (response.status === 200) {
if (response.data.code == 200) {
return response;
} else {
return Promise.reject(response.data);
}
return response;
}
},
function(error) {
//判斷請(qǐng)求超時(shí)
if (
error.code === "ECONNABORTED"
&& error.message.indexOf("timeout") !== -1
) {
app.$message.error("請(qǐng)求超時(shí),請(qǐng)刷新頁(yè)面重試!");
return;
}
error.seq = seq;
return Promise.reject(error);
}
);
seq++;
// return axios的new promise對(duì)象
return app.$axios(config).then(response => response.data);
注意點(diǎn):
- 這里的
app就是main.js中export出來(lái)的Vue實(shí)例。 - 超時(shí)的判斷的
錯(cuò)誤碼要和后臺(tái)溝通好。 - 最后把設(shè)置好的參數(shù)的
axios方法return出去。
4. 如何使用封裝好的axios
4.1 export需要使用的接口

上圖是我們常用的登出接口,文件上傳接口,獲取用戶列表接口。
4.2 頁(yè)面中調(diào)用


這里需要注意2點(diǎn)
-
notifyError和removeEmptyProp分別是處理接口返回錯(cuò)誤信息的處理函數(shù),以及移除空的篩選列表篩選參數(shù)的方法。notifyError我們馬上介紹。而這個(gè)removeEmptyProp等我下次bug改完的。O(∩_∩)O。 -
finally是Promise原型上的方法具體使用是和then,catch一致。這里我用它來(lái)處理無(wú)論接口請(qǐng)求成功或者失敗都關(guān)閉表格的加載loading。
tips:Promise的用法詳解我先挖個(gè)坑。下次記起來(lái)就來(lái)填。
4.3 接口錯(cuò)誤信息統(tǒng)一處理函數(shù)

// 引入錯(cuò)誤信息描述碼
import {
ERR_NET_FAIL,
ERR_API_NO_LOGIN,
message
} from "@/config/error";
// 統(tǒng)一錯(cuò)誤處理接口
export function notifyError(err) {
if (err.code) {
// 登錄超時(shí)返回登錄頁(yè)面
if (err.code === ERR_API_NO_LOGIN) {
// 處理3個(gè)接口同時(shí)調(diào)用超時(shí)的問(wèn)題
if (app.$route.name === "SignIn") {
return;
}
app.$message.error("登錄超時(shí),請(qǐng)重新登錄!");
app.$router.replace("/SignIn");
} else if (err.code === ERR_NET_FAIL) {
// 這里的Alert為封裝的插件彈窗
app.$Alert(message[ERR_NET_FAIL]);
} else {
// 其它狀態(tài)嗎輸出錯(cuò)誤提示
app.$Alert(err.message || message[ERR_NET_FAIL]);
}
} else {
app.$message.error(message[ERR_NET_FAIL]);
}
}
這里沒(méi)有啥子好講了,就是通過(guò)不同的code碼來(lái)執(zhí)行不同的操作,彈窗顯示錯(cuò)誤信息和登錄超時(shí)跳轉(zhuǎn)登錄頁(yè)面的一些處理。
4. 結(jié)語(yǔ)
終于擼完了,希望這篇文章能幫助到正在使用axios的你。
最后,喜歡的話請(qǐng)點(diǎn)個(gè)贊唄????。