前端封装 axios - 前端如何让接口调用更优雅
个人网站:aijianli.site/ 可以免费在线制作简历,提供PDF下载,方便快捷。
前端接口访问最常用的就是 axios ,基本上都会对 axios 进行封装后再使用,但是经历过的项目对 axios 的封装都是各不相同。以下介绍项目中遇到的 axios 使用的封装方式和自己探索出来的 axios 封装进行简单介绍。
一、项目中经历的 axios 封装方式
1.1 通用封装
首先,需要对 axios 进行请求拦截器和响应拦截器进行定义,这基本上是共识,每个项目中都会对 axios 加上请求拦截器和响应拦截器。如下:
import axios from "axios";
import {
Notification
} from "element-ui";
import qs from "qs";
// const baseUrl = "http://aijianli.site/api";
const baseUrl = "http://localhost:8081/api";
let config = {
baseURL: baseUrl,
timeout: 60000,
responseType: 'json',
headers: {
["Access-Control-Allow-Origin"]: '*',
['Pragma: no-cache']: false,
['Access-Control-Allow-Methods']: 'POST,GET,OPTIONS,DELETE',
['Access-Control-Allow-Headers']: 'x-requested-with,content-type'
}
};
const instance = axios.create(config)
//请求拦截器
instance.interceptors.request.use(
(config) => {
if (config.url !== "/api/user/login" && config.url !== "/api/user/regist") { // 判断请求是否是登录接口
config.headers.token = localStorage.getItem("token"); // 如果不是登录接口,就给请求头里面设置token
}
return config; // 返回这个配置对象,如果没有返回,这个请求就不会发送出去
},
(error) => {
return Promise.reject(error);
}
)
// 响应拦截器
instance.interceptors.response.use(
(res) => {
let code = res.data.state // 获取后端返回的状态码
if (code === 200) { // 成功
if (res.data.message) {
Notification({
message: res.data.message,
type: "success"
})
}
return res.data // 返回里面的数据,在使用这个axios时,获取到的东西就是这里返回的东西
} else {
Notification({
message: res.data.message,
type: "error"
})
return Promise.reject(res.data.message);
}
},
(error) => {
Notification({
message: "服务器错误:" + error.message,
type: "error"
})
return Promise.reject(error);
}
)
export default instance;
1.2 常规使用方式 - 直接将 axios 实例挂载到全局
常规的使用,在完成 1.1 步骤后就直接将 axios 实例挂载到一个全局可访问的地方就行。例如,在 Vue 项目中,直接将 axios 实例挂载到 Vue 原型上即可。如下:
import Vue from 'vue'
import App from './App.vue'
import ElementUI from 'element-ui';
Vue.use(ElementUI)
// 注册模块
import "./components/util-js/global-regist-module.js";
import axiosInstance from "./assets/js/axiosUtil.js";
Vue.config.productionTip = false
import "../node_modules/element-ui/lib/theme-chalk/index.css";
import "./assets/js/core.js";
import "./assets/css/cover-elementui-style.less";
import router from './router';
Vue.prototype.$axios = axiosInstance;
new Vue({
render: h => h(App),
router: router
}).$mount('#resume-creator-user')
1.3 组件内调用
在组件中直接通过 this.$axios 即可使用 axios 发送请求
// 调用后台接口
this.$axios.get("/xxx").then(res=> {
console.log(res)
})
// 调用后台接口
this.$axios.post("/xxx", data).then(res=> {
console.log(res)
})
1.4 存在问题
以上仅仅是对 axios 进行了请求拦截器和响应拦截器进行了封装。在使用时需要显示调用 get 还是 post 放在,同时还要添加接口访问地址 url ,这种使用方式在不同的地方调用同一个接口都得复制一下 url ,有一定的不便性,同时,如果后期有 url 调整时,需要修改的地方较多。因此在自己的项目中,做了优化。见 二、自己优化的 axios 封装方式
二、自己优化的 axios 封装方式
2.1 axios 封装后端接口
针对上述的使用方式存在的问题,个人的习惯是将所有后端接口封装在一起,前端调用接口时就像是使用本地方法一样调用接口,只需要关心方法参数即可,不需要关心后端地址、请求是 post 还是 get。具体实现如下:
import axios from "axios";
import {
Notification
} from "element-ui";
import qs from "qs";
// const baseUrl = "http://aijianli.site/api";
const baseUrl = "http://localhost:8081/api";
let config = {
baseURL: baseUrl,
timeout: 60000,
responseType: 'json',
headers: {
["Access-Control-Allow-Origin"]: '*',
['Pragma: no-cache']: false,
['Access-Control-Allow-Methods']: 'POST,GET,OPTIONS,DELETE',
['Access-Control-Allow-Headers']: 'x-requested-with,content-type'
}
};
const instance = axios.create(config)
//请求拦截器
instance.interceptors.request.use(
(config) => {
if (config.url !== "/api/user/login" && config.url !== "/api/user/regist") { // 判断请求是否是登录接口
config.headers.token = localStorage.getItem("token"); // 如果不是登录接口,就给请求头里面设置token
}
return config; // 返回这个配置对象,如果没有返回,这个请求就不会发送出去
},
(error) => {
return Promise.reject(error);
}
)
// 响应拦截器
instance.interceptors.response.use(
(res) => {
let code = res.data.state // 获取后端返回的状态码
if (code === 200) { // 成功
if (res.data.message) {
Notification({
message: res.data.message,
type: "success"
})
}
return res.data // 返回里面的数据,在使用这个axios时,获取到的东西就是这里返回的东西
} else {
Notification({
message: res.data.message,
type: "error"
})
return Promise.reject(res.data.message);
}
},
(error) => {
Notification({
message: "服务器错误:" + error.message,
type: "error"
})
return Promise.reject(error);
}
)
class axiosUtil {
constructor() {
this.axios = instance;
}
post(url, datas) {
return this.axios.post(url, datas)
}
get(url, datas) {
url = url + qs.stringify(datas, {
addQueryPrefix: true
})
return this.axios.get(url);
}
//在此封装所有的后端接口
/*-------------用户相关接口的开始-------------------------- */
/**
* 用户注册
* @param {*} data
* @returns
*/
regist(datas) {
return this.post("/user/regist", datas);
}
login(datas) {
return this.post("/user/login", datas);
}
queryUserByPage(datas) {
return this.post("/user/query/all", datas);
}
checkDeveloper(datas) {
return this.get("/user/check/developer", datas);
}
blacklistTiggle(datas) {
return this.get("/user/blacklist", datas);
}
querUserById() {
return this.get("/user/queryById", {});
}
updateUserById(datas) {
return this.post("/user/updateById", datas);
}
changePassword(datas) {
return this.post("/user/change/password", datas);
}
developerApply() {
return this.post("/user/developer/apply", {});
}
/*-------------用户相关接口的结束-------------------------- */
/*-------------字典相关的接口的开始----------------------------- */
/**
* 查询地区字典
* @returns 返回地区的第一层
*/
queryRegion() {
//地区字典的id
let dId = "xxx";
return this.queryByDid(dId);
}
/**
* 查询出民族字典
*/
queryNationality() {
//民族字典的id
let dId = "xxx";
return this.queryByDid(dId);
}
/**
* 查出性别字典
* @returns
*/
queryGender() {
let dId = "xxx";
return this.queryByDid(dId);
}
queryByDid(dId) {
return this.get("/dict/selectFirstLevel", {
dId
});
}
queryItemByParent(datas) {
return this.get("/dict/selectItemsByParent", datas);
}
/*-------------字典相关的接口的结束----------------------------- */
/*-------------简历信息相关的接口的开始--------------------------- */
/**
* 新增简历信息
* @param {*} datas
* @returns
*/
addResumeInfo(datas) {
return this.post("/resume/info/add", datas);
}
editResumeInfo(datas) {
return this.post("/resume/info/edit", datas);
}
deleteResumeInfo(datas) {
return this.get("/resume/info/delete", datas);
}
/**
* 根据用户id查用户简历信息列表
* @returns
*/
queryByUser(datas) {
return this.get("/resume/info/queryByUser", datas);
}
/**
* 根据简历信息id查出简历的所有信息
* 结构为:
* {
* baseInfo: {}, //基础信息
* }
*/
queryAllInfoById(datas) {
console.log(datas)
return this.get("/resume/info/queryById", datas)
}
/*-------------简历信息相关的接口的结束--------------------------- */
/*-------------简历模板接口开始------------------------ */
/**
* 通过用户信息查用户的模板,此功能只有开发者才有
* 没有参数,用户信息后端直接从token中取
*/
queryTemplateByUser() {
return this.get("/template/queryByUser", {});
}
// ...
/*-------------简历模板接口结束------------------------ */
/*--------------简历接口开始 --------------------------*/
addResume(datas) {
return this.post("/resume/add", datas)
}
// ....
/*--------------简历接口结束 --------------------------*/
}
export default new axiosUtil();
如上代码,将 axios 的实例对象封装在 axiosUtil 中
class axiosUtil {
constructor() {
this.axios = instance;
}
// ...
}
对 axios 的 get,post 方法再封装
class axiosUtil {
constructor() {
this.axios = instance;
}
post(url, datas) {
return this.axios.post(url, datas)
}
get(url, datas) {
url = url + qs.stringify(datas, {
addQueryPrefix: true
})
return this.axios.get(url);
}
}
业务接口直接调用封装后的 get, post, 外部使用只需要关心参数即可。将后端所有接口封装在此,当然,如果接口较多,也可以采用分模块封装的方式。
//在此封装所有的后端接口
/*-------------用户相关接口的开始-------------------------- */
/**
* 用户注册
* @param {*} data
* @returns
*/
regist(datas) {
return this.post("/user/regist", datas);
}
login(datas) {
return this.post("/user/login", datas);
}
queryUserByPage(datas) {
return this.post("/user/query/all", datas);
}
2.2 接口调用
完成以上封装,并将 axiosUtil 工具类挂载到 Vue 上,或者也可以放全局对象。在我自己的项目中是将 axiosUtil 工具类对象放在全局的对象 core.js 中,在调用时就像是调用一个本地方法一样。如下:
core.js
import axios from "./axiosUtil.js";
class Core {
constructor() {
// 封装请求的 axios 在此
this.$axios = axios;
}
// ...
}
window.core = new Core();
组件中使用: 以下两个例子,接口调用就像是调用本地的一个瓶Promise方法,不需要关心 url 和 请求方式(get,post,delete...)
core.$axios.regist(this.formData)
this.$axios.queryAvailableTemplate().then(res => {
let templates = res.data;
templates.forEach(template => {
// 如果当前模板没有实例存在,才加载当前模板的实例
if (!this.$instances[template.t_url]) {
// 加载模板 js
this.loadProdTemplate(template.t_url);
}
})
})
2.3 存在问题
以上封装方式,虽然能够解决前端不需要重复写 url ,并且明确指定方法调用的是 get 还是 post 的问题,但是对于多人合作的项目来说,每次后端新增接口之后,就需要在 axiosUtil 中新增接口,维护起来不是特别方便。
三、解决想法
由后端进行封装,将 二 中封装的代码作为模板,由后端写脚本在每次新增接口或者接口变更时,将所有接口按模板的方式写入 js 文件,并将文件接口 js 文件直接部署到服务器上,前端使用时通过 script 标签引入。如此便能实现接口自动维护。
转载自:https://juejin.cn/post/7296847748102422528