网络请求是每个项目都需要具备的功能,现在的 vue 项目中基本都是使用的 axios 作为网络请求库,老规矩,先上一份 axios中文文档
axios 本身已经封装的很好了,直接使用也是没问题的,为了在实际开发中更好更方便的使用,需要对它进行一下二次封装。二次封装要达到的效果:
1. 请求开始时,弹出加载动画,如超时,弹出提示并结束动画
2. 正常拿到请求结果后结束动画,
3. 如请求失败,弹出失败信息并结束动画
4. 请求正常,但返回的结果不对,根据返回的错误码跳转到指定页面,并弹出错误信息。比如需要登录后才能请求的接口,没有登录的话跳转到登录
5. 对常用的 get,post 请求进行封装,让它们更好用
以上,就是我们进行封装的目的和预期要达到的效果,
下面开始封装,网络请求的封装都是根据服务器返回的数据格式来进行封装的,先说下我们后台返回的数据格式,
//正常情况
{
code:0,
data:{
......
},
time:时间戳
}
//请求有问题,返回错误信息
{
code:0,
text:"......"
time:时间戳
}
上面是我们的后台返回的数据格式,但是在实际返回的数据不是这样的,它外面还有一层,那外面的一层是 axios 的 response 数据结构,结构如下:
{
// `data` 由服务器提供的响应
data: {
//这里面才是我们服务器返回的真正数据
......
},
// `status` 来自服务器响应的 HTTP 状态码
status: 200,
// `statusText` 来自服务器响应的 HTTP 状态信息
statusText: 'OK',
// `headers` 服务器响应的头
headers: {},
// `config` 是为请求提供的配置信息
config: {}
}
返回的数据结构有了就好办了,下面就直接贴出最终的封装代码,关键地方都有注释,在封装的这个 JS 里面,使用了 mint-ui 的二个组件,一个是 Indicator 加载动画,一个是 toast ,toast 已经在 main.js 里面全局引用过了,所以这里面是没有 import toast 的,这点注意
import axios from 'axios'
import { Indicator } from 'mint-ui'//加载中动画
//vue 实例
let vue = null
//是否允许显示toast
let showToast = true
//请求开始时,开启加载中动画,出错了提示并关闭动画
axios.interceptors.request.use(config => {
Indicator.open()
return config
}, error => {
if (vue && showToast) {
vue.toast('请求超时!')
}
Indicator.close()
return Promise.reject(error)
})
//请求完成时,关闭加载中动画,返回数据或错误信息
axios.interceptors.response.use(response => {
Indicator.close()
//一切正常,返回数据或空对象
if (response.data.code === 0) {
return response.data.data || {}
} else {
//没有数据,只有提示信息,则弹出提示信息,
if (response.data.text != null && response.data.text.length > 0) {
if (vue && showToast) {
vue.toast(response.data.text)
}
}
}
}, error => {
Indicator.close()
if (error.response) {
if (error.response.data.code === 10 || error.response.data.code === 6) {
//未登录
if (vue) {
vue.$router.replace('/login')
}
}
// 请求已发出,但服务器响应的状态码不在 2xx 范围内,有错误信息则弹出错误信息
console.log('response-error-data', error.response.data)
if (error.response.data.text != null && error.response.data.text.length > 0) {
if (vue && showToast) {
vue.toast(error.response.data.text)
}
}
} else {
//什么数据都没有,直接出错了
console.log('Error', error.message)
if (vue && showToast) {
vue.toast('网络出错了,未请求到数据')
}
}
})
export default class api {
static get = (url, vueContext, isShow) => {
showToast = true
if (vueContext != null) {
vue = vueContext
}
if (isShow === false) {
showToast = isShow
}
return axios({
method: 'get',
url: `${BASE_URL}${url}`,
withCredentials: true,//表示跨域请求时是否需要使用凭证
headers: {
'X-Requested-With': 'XMLHttpRequest',
},
})
}
static post = (url, params, vueContext, isShow) => {
showToast = true
if (vueContext != null) {
vue = vueContext
}
if (isShow === false) {
showToast = isShow
}
return axios({
method: 'post',
url: `${BASE_URL}${url}`,
data: params,
withCredentials: true,//表示跨域请求时是否需要使用凭证
// 发送请求前处理request的数据
transformRequest: [
function (data) {
let ret = ''
for (let it in data) {
ret += encodeURIComponent(it) + '=' + encodeURIComponent(data[it]) + '&'
}
return ret
}],
headers: {
'X-Requested-With': 'XMLHttpRequest',
'Content-Type': 'application/x-www-form-urlencoded',
},
})
}
};
经过上面的封装之后,使用就简单了,只需要处理请求成功的回调就行了,其他的请求错误等等情况都可以不用管,这里已经都处理好了。
还有最后一步就是把上面 export 出来的 get ,post 方法在 main.js 中挂载到 vue 的原型上,这样就能整个项目中随意使用了。
import api from './utils/apiHelp'
Vue.prototype.api = api
比如说发起登录请求:
this.api.get('/index/trend',this).then((data) => {
if (data && data.indices) {
this.indices = data.indices
}
})
So Easy!