在前端开发中,要与后端完成数据交互,我们通常需要借助一下Ajax的http库来完成与后台数据接口的对接,jQuery
盛行时代,我们会使用$.ajax()
来实现交互,现如今已有许多现成的http库,例如:SuperAgent
、Axios
、Fetch
…等等。有了这些http库我们不需要太多的去了解Ajax底层的工作原理,只需要关注一个请求的request的组成以及如何处理一个response即可,虽然一定程度上简化了交互,但这样在实际应用中还是远远不够的
今天来教教大家如何使用Axios封装一个完整的请求交互模块:
首先封装前我们应该思考:一个请求发起前应该做什么,请求得到响应后应该做什么,一般来说,请求前我们会设定一下公共的请求headers和请求根地址,请求时需要展示一个表示正在Loding的组件,响应回来后,根据后端返回的请求结果,判断一下请求是否成立,成功的话就按照业务逻辑来处理,不成立就需要展示后端返回的错误信息等等,以上这些公共的处理,我们都应当封装起来,而不应该在单独一个请求里重复编写
好吧,我们开始进入正文:
单独创建一个api.js文件:
先定义公共头部和根地址
//导入依赖模块
import axios from "axios";
import Router from "../router";
import qs from "qs";
import store from '../store'
// 默认的根地址,一般来说会有一个测试环境和生产环境
axios.defaults.baseURL = "https://test.com"; // 测试环境
// axios.defaults.baseURL = "https:/product.com"; // 线上环境
//设置公共请求头部
//axios.defaults.headers.A=""
//axios.defaults.headers.B=""
如果引入了vueX, store.js文件中定义一个isLoading状态:
export default new Vuex.Store({
state: {
isLoading: false, //请求加载状态
},
mutations: {
setLoading(state, data) {
state.isLoading= data
},
}
})
请求前后的处理:
// request 拦截器
axios.interceptors.request.use(
config => {
// 登录后的token定义至headers
if(localStorage.getItem("loginTk"))
config.headers.token= localStorage.getItem("loginTk");
// 使用vueX来实现loading组件的展示
store.commit('setLoading',true);
return config;
},
error => {
return Promise.reject(error);
}
);
//以我这个项目作为例子,后端返回的结果是这种形式
{
code:200, // 响应状态码 200-请求成立 4001-登录过期 400-请求异常
data:"" // 响应数据
msg:"" // 响应信息
}
//response 拦截器
axios.interceptors.response.use(
response => {
// 隐藏loding组件
store.commit('setLoading',false)
// 请求成功
if( response.data.code ===200 )
{
return response;
}
// 登录失效
else if (
response.data.code === 40001
) {
localStorage.removeItem("loginTk");
Router.push({ name: "login" });
return false;
}
// 请求异常
else{
message.error(response.msg);
}
},
error => {
return Promise.reject(error);
}
);
loding组件定义在根路由App.vue里:
<template>
<div id="app" >
<router-view />
<div v-if="$store.state.isLoading">
需要展示的loading组件
</div>
</div>
</template>
再根据请求方式和数据类型进行封装:
/*
* 封装get方法
* @param url
* @param params
* @returns {Promise}
*/
export function fetch(url, params = {}) {
return new Promise((resolve, reject) => {
axios
.get(url, {
params: params,
headers: { "Content-Type": "application/x-www-form-urlencoded" }
})
.then(response => {
resolve(response.data);
})
.catch(err => {
reject(err);
});
});
}
/*
* 封装post请求
* @param url
* @param data
* @returns {Promise}
*/
// json格式
export function post(url, data) {
return new Promise((resolve, reject) => {
axios.post(url, data).then(
response => {
resolve(response.data);
},
err => {
reject(err);
}
);
});
}
// formData格式
export function From(url, data) {
return new Promise((resolve, reject) => {
axios
.post(url, qs.stringify(data), {
headers: { "Content-Type": "application/x-www-form-urlencoded" }
})
.then(
response => {
resolve(response.data);
},
err => {
reject(err);
}
);
});
}
到这里差不多就封装完成了,但我们应该如何简单使用呢?
进入main.js文件修改:
import Vue from "vue";
// 导入封装后的请求模块
import { post, fetch, From, } from "./api";
//将封装后的请求方法注入至Vue实例原型
Vue.prototype.$post = post;
Vue.prototype.$fetch = fetch;
Vue.prototype.$From = From;
然后你哪里需要请求直接通过this.$方法名调用即可:
this.$(方法名) (url,{请求参数}).then(res=>{
if(res){
// 处理逻辑
}
})
// 针对请求结果我们已经做了处理,只需要判断有无res,有的话那请求就是成立的
到这里差不多就大功告成了,经过一系列封装后我们只需要对正确的请求进行处理,其他的都不用管了,这样写起来是不是很爽?
拓展:
有人肯定会问,如果我这个项目不需要vueX,就为了展示一个loding组件就引入进来是不是有点浪费?
如果不需要vueX,你可以这样做:
改写App.vue:
<template>
<div id="app" >
<router-view />
<div v-if="isLoading">
需要展示的loading组件
</div>
</div>
</template>
<script>
import qs from "qs";
import Vue from "vue";
import axios from "axios";
export default {
name: "App",
data() {
return {
isLoading: false,
};
},
created() {
// request 拦截器
axios.interceptors.request.use(
config => {
//改成data中的isLoding
this.isLoading=true
return config;
},
error => {
return Promise.reject(error);
}
)
//response 拦截器
axios.interceptors.response.use(
response => {
// 隐藏loding组件
this.isLoading=false
// 请求成功
if( response.data.code ===200 )
{
return response;
}
// 登录失效
else if (
response.data.code === 40001
) {
localStorage.removeItem("loginTk");
this.$router.push({ name: "login" });
return false;
}
// 请求异常
else{
message.error(response.msg);
}
},
error => {
return Promise.reject(error);
}
);
Vue.prototype.$post = this.post;
Vue.prototype.$fetch = this.fetch;
Vue.prototype.$From = this.From;
},
methods: {
fetch(url, params = {}) {
return new Promise((resolve, reject) => {
axios
.get(url, {
params: params,
headers: { "Content-Type": "application/x-www-form-urlencoded" }
})
.then(response => {
resolve(response.data);
})
.catch(err => {
reject(err);
});
});
},
post(url, data) {
return new Promise((resolve, reject) => {
axios.post(url, data).then(
response => {
resolve(response.data);
},
err => {
reject(err);
}
);
});
},
From(url, data) {
return new Promise((resolve, reject) => {
axios
.post(url, qs.stringify(data), {
headers: { "Content-Type": "application/x-www-form-urlencoded" }
})
.then(
response => {
resolve(response.data);
},
err => {
reject(err);
}
);
});
}
}
};
</script>
所以的封装函数都定义至在根路由里,在created周期里注入原型