1. 前言
hello,大家好,终于改完
bug
了。现在就开始来填坑了。在上一篇文章如何优雅的使用Vuex中提到了使用axios
插件封装的一个统一的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);
}
});
当然这种使用方法是再与后台进行交互方面是没有什么问题,但是在接口如果繁多并且不止再一个路由中使用时,一旦涉及到接口参数
或者接口url地址
的修改就会让人很崩溃。并且也没做统一的错误返回处理。前前后后接手的人也很多,所以就出现有的错误是弹窗提示,有的错误是页面上方的message
。有的就干脆不给提示。
我当时也有问交接的同事为啥不做一下封装,谁知道这位年轻人
啪
的一下就站起来,很快啊
,上来就是一个不知道
,翻手一个没必要
。我大意了
,还没来得及反驳,他人就溜了。为了后面自己不再踩坑,我就开始着手进行接口封装。
3. 如何封装axios
我这篇文章主要讲解如何封装axios
,前提是你已经熟悉了如何在v-cli
脚手架中使用axios
。还不了解的小伙伴可以移步axios官方文档中学习axios
的具体使用。
3-1. 修改基础配置
- 首先我们通过
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. 创建api.js
接口库
3.2-1. 我们先在src
文件夹下新增utils
文件夹,新建api.js
文件,编辑该文件
// 引入Vue实例对象
import { app } from "../main";
// 设置请求次数
let seq = 0;
// 设置基础的统一接口方法
function axiosFn(options) {
const config = {
// 设置请求头和默认请求方式
// 默认为get请求
method: "get",
// 设置请求头方式
headers: {
"Content-Type": "application/json;charset=utf-8",
}
};
// 这里是对传入的基础配置进行处理
for (const k in options) {
if (k !== "param" && options.hasOwnProperty(k)) {
config[k] = options[k];
}
}
// 设置不同请求的参数
.....
}
第一步主要是设置基础的配置,包括
请求头
和请求方式
的设置。
3.2-2. 根据请求方式和请求头的不同设置不同传参方式
// 设置不同请求的参数
if (
config.method === "post"
|| config.method === "put"
|| config.method === "delete"
) {
// 处理formData数据上传
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的请求参数传递
if (config.headers["Content-Type"] === "application/json;charset=utf-8") {
config["data"] = options.param;
}
// 请求头为application/x-www-form-urlencoded的请求参数传递
if (config.headers["Content-Type"] === "application/x-www-form-urlencoded") {
let postData = "";
for (const key in options.param) {
// 设置传参为数组时使用jsonStringfy方法转字符串
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"];
}
// 拦截请求结果
.....
注意
:这里的代码本来是没有这么复杂的,最开始只是设置最基础的get
,post
请求参数。后来因为这个api
库被他们拿去用到各个项目中。出现了一大堆的问题。但是他们自己又不会针对项目作出对应的修改。一出问题就喷我封装的不对,难用。我又不能和他意气用事。只有一张图表达我的心情。
咳咳,回到上述代码中,这里
get
请求涉及到的处理不多,主要是其他的请求和请求头的不同的处理
-
get
请求处理:主要是对params
参数中数组
和对象
进行JSON.stringify
转码就好了。这个是我们这边的与后台约定的统一处理方法。大家如果没有这方面的需求可以不加这层判断。 -
post/put/delete
请求处理:这里主要是对不同的请求头
进行处理。典型的处理是针对上传文件
的处理。通过这层处理后,我们在联调上传
接口时,就不用使用new FormData()
去包装我们的上传参数了。其它两种也是常用的请求头
判断和处理。这个就看具体的使用项目了。
3.2-3. 设置axios
拦截器
// 拦截请求结果
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
实例。 - 超时的判断的
错误码
要和后台沟通好。 - 最后把设置好的参数的
axios
方法return
出去。
4. 如何使用封装好的axios
4.1 export
需要使用的接口
上图是我们常用的登出
接口,文件上传
接口,获取用户列表
接口。
4.2 页面中调用
这里需要注意2点
-
notifyError
和removeEmptyProp
分别是处理接口返回错误信息的处理函数,以及移除空的筛选列表筛选参数的方法。notifyError
我们马上介绍。而这个removeEmptyProp
等我下次bug
改完的。O(∩_∩)O。 -
finally
是Promise
原型上的方法具体使用是和then
,catch
一致。这里我用它来处理无论接口请求成功或者失败都关闭表格的加载loading
。
tips
:Promise
的用法详解我先挖个坑。下次记起来就来填。
4.3 接口错误信息统一处理函数
// 引入错误信息描述码
import {
ERR_NET_FAIL,
ERR_API_NO_LOGIN,
message
} from "@/config/error";
// 统一错误处理接口
export function notifyError(err) {
if (err.code) {
// 登录超时返回登录页面
if (err.code === ERR_API_NO_LOGIN) {
// 处理3个接口同时调用超时的问题
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 {
// 其它状态吗输出错误提示
app.$Alert(err.message || message[ERR_NET_FAIL]);
}
} else {
app.$message.error(message[ERR_NET_FAIL]);
}
}
这里没有啥子好讲了,就是通过不同的code
码来执行不同的操作,弹窗显示错误信息
和登录超时
跳转登录页面
的一些处理。
4. 结语
终于撸完了,希望这篇文章能帮助到正在使用axios
的你。
最后,喜欢的话请点个赞呗❤️❤️。