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

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

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


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

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