vue中axios请求优化+axios封装成最简效果 + axios不支持finally的解决办法

一、问题需求来源:

1.随着项目代码量加大,功能需求复杂化,对工具的封装成立必不可少的杀手锏。
如果项目中有200个axios请求,封装后可以节省10行,那整个项目就节省了2000行代码,可维护性也增强了。
2.请求数据时要使用loading加载提示,每次请求结束后无论成功失败或异常,都要关闭loading,但axios中只有then、catch,没有finally,作为强迫症的我又不想在then、catch中写两遍关闭loading。
于是,在封装的时候想了办法解决这个问题。

先展示一下封装后的效果
vue中axios请求优化+axios封装成最简效果 + axios不支持finally的解决办法_第1张图片

二、axios请求演化

整个文章以get请求作展示,post请求大相径庭就不作详细介绍了,最后给个示例代码

  1. 最原始的axios是这样的
axios.get('/user', {
    params: {
      ID: 12345
    }
  })
  .then(response => {	// 正常请求
    this.tableData = response.data;
  })
  .catch(error => {	// 出现异常
    console.log(error);
  });
  1. 因为请求后即使没出现异常,也可能会出现请求没获取到任何数据的情况,这时候要加些判断过滤了,还有如果出错了要给用户提示
axios.get('/user', {
    params: {
      ID: 12345
    }
  })
  .then(response => {	// 正常请求
    if(!response || !response.data){
         this.warnMsg("数据获取失败!");
         this.tableData = [];
    }else if(result.data.status == 0){
         this.tableData = result.data;
    }
  })
  .catch(error => {	// 出现异常
    this.errMsg("系统异常,数据获取失败!");
    console.log(error);
  });

这里的this.warnMsg()和this.errMsg()是自己封装的全局方法,用于弹出Element-ui的消息提示
vue中axios请求优化+axios封装成最简效果 + axios不支持finally的解决办法_第2张图片
3.现在为了进一步优化用户友好性,后端对相应数据进行了封装,如果请求的参数错误或一些其他原因无法成功,会给些提示。
后端返回数据格式
{
“status”: 0, // 请求状态 常用:0表示成功 1表示失败
“msg”: “成功”, // 后端提示消息,如果失败了,通过此字段返回失败原因
“data”: [] // 得到的数据
}
前端代码也对应变为:

axios.get('/user', {
    params: {
      ID: 12345
    }
  })
  .then(response => {	// 正常请求
    if(!response || !response.data){	// 未获取到数据
         this.warnMsg("数据获取失败!");
         this.tableData = [];
    }else  if(result.data.status == 0){	// 后端返回成功
         this.tableData = result.data.data;
    }else {
         this.warnMsg(result.data.msg);	// 后端返回失败
         this.tableData = [];
    }
  })
  .catch(error => {	// 出现异常
    this.errMsg("系统异常,数据获取失败!");
    console.log(error);
  });

4.请求数据总会有网络延迟,如果延迟大了,就会让用户觉得系统卡机了,加上加载动画会看让系统看起来流畅性增强了。
这里用了Element-ui 的loading加载效果(整个项目使用的是vue+Element-ui)
vue中axios请求优化+axios封装成最简效果 + axios不支持finally的解决办法_第3张图片
请求前开启loading动画,不管请求成功或失败,请求结束后都要关闭loading
这时的代码显得有点臃肿了

this.loading = true;	// 开启加载动画
axios.get('/user', {
    params: {
      ID: 12345
    }
  })
  .then(response => {	// 正常请求
    if(!response || !response.data){	// 未获取到数据
         this.warnMsg("数据获取失败!");
         this.tableData = [];
    }else  if(result.data.status == 0){	// 后端返回成功
         this.tableData = result.data.data;
    }else {
         this.warnMsg(result.data.msg);	// 后端返回失败
         this.tableData = [];
    }
    this.loading = false;	// 关闭loading动画
  })
  .catch(error => {	// 出现异常
    this.errMsg("系统异常,数据获取失败!" + error);	// 将错误信息直接显示给用户看
    this.loading = false;	// 关闭loading动画
  });

一个功能完整的get请求,已达二十多行代码,而且每次使用时都会产生很多相同的代码,这个时候就想要自己封装成

三、axios封装

动手开始封装
1.建立一个htttp.js 在这之前要先安装和引入axios、element、qs

import axios from 'axios';
import { Message } from 'element-ui';
import router from './router' // 引入路由
import qs from 'qs' // 使用qs对post传送的据序列化

axios.defaults.baseURL = "http://127.0.0.1:31706/" // 默认连接地址
axios.defaults.timeout = 30000; // 响应时间
axios.defaults.withCredentials = true;
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=UTF-8'; //配置请求头



/**
 * http状态 status
 * 200 登录成功、退出登录成功
 * 210 登录失败
 *
 * 401 未登录、登录超时
 * 403 权限不足等原因禁止访问
 */
// 拦截器
axios.interceptors.response.use(
    response => {
        if (response.data) {
            if (response.data && String(response.data).substring(0, 15) == "") {
                Message("请重新登录!");
                router.push({
                    path: '/',
                    query: { redirect: router.currentRoute.fullPath }
                })
            }
        }
        return response;
    },
    error => {
        if (error.response.data) {
            if (error.response.data.status == 401) {
                Message(error.response.data.message);
                router.push({
                    path: '/',
                    query: { redirect: router.currentRoute.fullPath }
                })
            } else if (error.response.data.status == 403) {
                Message(error.response.data.msg);
            }
        }
        return Promise.reject(error.response.data);
    }
)

/**
 * 封装get请求
 */
export function get(url, params = {}) {
    return new Promise((resolve, reject) => {
        axios.get(url, { params: params })
            .then(response => {
                if (!response || !response.data) {
                    Message("数据获取失败!");
                    resolve({ ok: false, data: null });
                } else if (response.data.status == 0) {
                    resolve({ ok: true, data: response.data.data });
                } else {
                    Message(response.data.msg);
                    resolve({ ok: false, data: null });
                }
            }).catch(err => {
                Message("系统异常!" + err);
                resolve({ ok: false, data: null });
            })
    })
}

// 此处省略post请求,如果需要请在评论区留言

2.在main.js中引入

import axios from 'axios'
import {get,post} from './http'
//定义全局变量
Vue.prototype.$get=get;
Vue.prototype.$post=post;

3.在项目中使用

this.loading = true;	// 开启加载动画
this.$get('/user', { ID: 12345 }).then(response => {
    this.tableData = response.ok? response.data : [];
    this.loading = false;	// 关闭加载动画
})

铛铛铛~ 一下子少了四分之三的代码
封装完后,只需要关闭一次loading即可

四、封装详解

上面的代码是封装好的,可能会不理解为什么封装完,axios使用的方式也变了,下面详细介绍一下封装的思路

  1. 一开始的封装其实是这样的
http.js
// get方法封装
export function get(url,params={}){
  return new Promise((resolve,reject) => {
    axios.get(url,{
      params:params
    })
    .then(response => {
      resolve(response.data);
    })
    .catch(err => {
      reject(err)
    })
  })
}

项目文件.vue
// 使用
this.$get('/getList')
   .then(response => {
     console.log(response)
   }).catch(error => {
     console.log(error );
   })

现在好理解了吧,封装方法跟原生axios差不多
封装过程用到了Promise,如果想了解更多可以参考https://www.jianshu.com/p/67a6cade05f2

使用axios时Promise有且仅有两个回调参数resolve和reject,一个用于正常执行后回调,一个用于异常后回调

  1. Promise中有finally,可以加在后面:
return new Promise((resolve,reject) => {
    axios.get(url,{
      params:params
    })
    .then(response => {
      resolve(response.data);
    })
    .catch(err => {
      reject(err)
    }).finally(console.log("执行finally"))

但是这里Promise只会回调一次,如果在finally进行了回调,then和catch里面的回调就不会执行
很失望,还是没有找到合适的办法

  1. 通过多次尝试,发现不管resolve或reject放在axios的then还是catch或之外的地方,使用封装的get请求时resolve只会作用于then,reject只会作用于catch,有了这个理解,就产生了新的思路

现在错误提示信息都在封装方法里面放出了,对于回调功能,只需要告诉它请求成功或失败、请求的数据,并且不管成功或失败都可以执行一段代码,以此关闭loading

那我们就直接自己定义一个对象用于回调时返回所需数据吧,这回都回调then

.then(response => {
     if (!response || !response.data) {
         Message("数据获取失败!");
         resolve({ ok: false, data: null });	// 回调then,返回失败提示
      } else if (response.data.status == 0) {
         resolve({ ok: true, data: response.data.data });	// 回调then,请求得到的数据
      } else {
         Message(response.data.msg);
         resolve({ ok: false, data: null });	// 回调then,返回失败状态
      }
}).catch(err => {
     Message("系统异常!" + err);
     resolve({ ok: false, data: null });	// 回调then,返回失败状态
 })

这样就保证了不管成功或失败,都会回调then,只需在then里面写一次关闭即可

完整代码已在第三部分给出,如果有问题请在评论区提问

你可能感兴趣的:(vue,前端,axios,vue)