当前位置:   article > 正文

vue2 vue3 通用的axios的二次封装,实现功能、业务、配置分离,配置传递的是url跟请求方法get,post,patch,delete;业务传递的是业务对象以及对象的格式(formdata)_vue3 axios二次封装请求接口

vue3 axios二次封装请求接口

Vue 笔记

本人是一个web前端开发工程师,主要是vue框架,整理了一些Vue常用的技术,一方面是分享,一方面是做总结,今后也会一直更新,有好建议的同学欢迎评论区分享 ;-)

Vue专栏:点击此处
Vue组件库专栏:点击此处
Vue2 vs Vue3 专栏:点击此处
Typescript专栏:点击此处

在这里插入图片描述



前言

使用的网络请求库是axios
axios 官方网址:点击此处

对于后台管理系统来说,常用的就是增删改查,虽然代码量不是很多,但是也不是很舒服,所以对其进行了二次封装,实现功能与业务与配置分离。

  • 配置传递的是url跟请求方法get,post,patch,delete;
  • 业务传递的是业务对象以及对象的格式(formdata),以及axios的其他配置;
  • 功能主要是将配置转化成http对象,并且设置了axios通用配置,以及axios 请求以及响应拦截器。

按照自己的需求还可以添加其他的配置

  1. 封装get请求,传递对象,让其自动转化为url params 传参方式;
  2. 还有…

这边主要根据自身项目的需求,自行修改部分功能分离代码即可...


项目目录

在这里插入图片描述


源码分析

主要从配置,业务,功能 三方面进行讲解。


1. 配置分离

导出了一个api 对象

  • 第一层对象:对应每一个页面,比如用户管理
  • 第二次对象:每一个页面对应的接口,常规的增删改查,当然还可以有deleteID,这个是自己定义的,主要是用来做兼容,毕竟有些公司删除也是用了params的方式(htttp://www.test.deleteUser?id=111)或者(htttp://www.test.deleteUser/111)

自定义只需要去修改功能分离中的数据即可...

const api_obj = {
  // 登陆
  loginPage: {
    login: {
      method: 'post',
      url: '/server/login'
    }
  },
  // 用户管理
  usersManager: {
    // 增
    add: {
      method: 'post',
      url: '/server/users/add'
    },
    // 删
    delete: {
      method: 'deleteID',
      url: '/server/users/delete'
    },
    // 改
    update: {
      method: 'patch',
      url: '/server/users/update'
    },
    // 查
    getList: {
      method: 'get',
      url: '/server/users/getLists'
    },
  }
}

export default api_obj;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

2. 业务分离

业务这边主要是选择对应的http对象的API,然后可以传递参数

  1. 第一个参数就是传递给后端的对象;
  2. 第二个参数就是传递对象是普通的json格式,还是formData格式(用于传递文件);
  3. 第三个参数就是额外的axios config。
Http[index][key] = function (
                        params, // get/delete为urlparams,post/patch为data
                        isFormData = false, // 默认是false,如果是post或者patch还要判断是否为formData
                        config = {}
                    ) {//...}
  • 1
  • 2
  • 3
  • 4
  • 5

2.1 vue3 组合式API

main.ts 引入

// 导入自定义axios
import myAxios from "./myAxios/index";

app.config.globalProperties.$http = myAxios.main;
app.config.globalProperties.$axios = myAxios.global;
  • 1
  • 2
  • 3
  • 4
  • 5

xxx.vue 引用

// 部分代码...
import { getCurrentInstance } from "vue";
// vue3导入全局属性
const { proxy } = getCurrentInstance() as any;
const http = proxy.$http.userManager;

// 查找数据
async function getList() {
  // 判断是否有对象,没有的话就自动弄
  let res = await http.getList({
    // ...
  });

  if (res) {
  	// ...
  }
}

// 新增,编辑数据
function saveItem(form: any) {
  if (form.id) {
    http.update(form).then((res: any) => {
  	// ...
  	// 修改数据
    });
  } else {
    http.add(form).then((res: any) => {
  	// ...
  	// 新增数据
    });
  }
}

// 删除
function deleteItem(row: any) {
  http.delete(row.id).then((res: any) => {
  	// ...
  	// 删除数据
  });
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

2.2 vue2 选项式API

main.js

// 导入自定义axios
import myAxios from './myAxios';
Vue.prototype.$myAxios = myAxios.main;
Vue.prototype.$globalAxios = myAxios.global;
  • 1
  • 2
  • 3
  • 4

xxx.vue

<template>
  <div></div>
</template>

<script>
export default {
  data() {
    return {
      // axios请求
      http: this.$myAxios.yyn_api.usersManager,
    };
  },
  methods: {
    // 查找数据
    async getList() {
      // 判断是否有对象,没有的话就自动弄
      let res = await this.http.getList({
        // ...
      });

      if (res) {
        // ...
      }
    },

    // 新增,编辑数据
    saveItem(form) {
      if (form.id) {
        this.http.update(form).then((res) => {
          // ...
          // 修改数据
        });
      } else {
        this.http.add(form).then((res) => {
          // ...
          // 新增数据
        });
      }
    },

    // 删除
    deleteItem(row) {
      this.http.delete(row.id).then((res) => {
        // ...
        // 删除数据
      });
    },
  },
};
</script>

<style lang="scss" scoped></style>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53

3. 功能分离

3.1 根据配置,生成对象数组

http 是一个用于存放API的对象,是个二维数组http[index][key],第一个所以代表配置所对应的页面,第二个所以代表该页面的一个请求,如:http[‘userManager’][‘getList’],代表着,用户请求页面中获取用户列表~~~

部分代码

import axios from "axios";
import api_obj from "./api_obj";
import router from "../router";

// 实例化一个axios实例
let instance = axios.create({
  timeout: 60000,
});

function formData(temp_api) {
  let Http = {};
  for (const index in temp_api) {
    if (temp_api.hasOwnProperty(index)) {
      let apiTemp = temp_api[index];
      Http[index] = {};
      for (const key in apiTemp) {
        if (apiTemp.hasOwnProperty(key)) {
          let api = apiTemp[key]; // 获取method跟url
          Http[index][key] = function (
            params, // get/delete为urlparams,post/patch为data
            isFormData = false, // 默认是false,如果是post或者patch还要判断是否为formData
            config = {}
          ) {
            return new Promise(function (resolve, reject) {
              // window.console.log(key);
              let newParams = null;
              // 判断是否为formData数据
              if (isFormData && params) {
                let formData = new FormData();
                for (const key in params) {
                  if (params.hasOwnProperty(key)) {
                    formData.append(key, params[key]);
                    newParams = formData;
                  }
                }
              } else {
                newParams = params;
              }

              // 判断不同的请求,不同的接口
              if (api.method === "post" || api.method === "patch") {
                instance[api.method](api.url, newParams, config)
                  .then((res) => {
                    resolve(res);
                  })
                  .catch((err) => {
                    reject(err);
                  });
              } else if (api.method === "get" || api.method === "delete") {
                config.params = { ...params, ...api.params };
                instance[api.method](api.url, config)
                  .then((res) => {
                    resolve(res);
                  })
                  .catch((err) => {
                    reject(err);
                  });
              } else if (api.method === "put") {
                instance[api.method](api.url + params, config)
                  .then((res) => {
                    resolve(res);
                  })
                  .catch((err) => {
                    reject(err);
                  });
              } else if (api.method === "deleteID") {
                instance["delete"](api.url + "/" + params, config)
                  .then((res) => {
                    resolve(res);
                  })
                  .catch((err) => {
                    reject(err);
                  });
              }
            });
          };
        }
      }
    }
  }
  return Http;
}
var Https = {};
Https = formData(api_obj);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84

3.2 axios 拦截器

请求拦截器,请求前,添加token

部分代码

instance.interceptors.request.use(config => {
    let temp = JSON.parse(window.sessionStorage.getItem('user'));
    if (temp) {
        config.headers.common['authorization'] = 'Zyp ' + temp.token;
    } else {
        config.headers.common['authorization'] = 'Zyp ';
    }

    // 去除空字符串数据
    for (let key in config.data) {
        if (!config.data[key] && typeof config.data[key] == 'String') {
            config.data[key] = null;
        }
    }
    return config;
}, (error) => {
    return Promise.reject(error)
})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

响应拦截器,接受到服务器返回的数据,判断数据是否合法,合法的话就传递给业务层,不合法,则在响应拦截器中进行处理

部分代码

instance.interceptors.response.use(response => {
    if (!response.data || !response.data.code) {
        Message({
            showClose: true,
            message: '后端返回格式不对!',
            type: 'error'
        });
        return Promise.reject(response.data.message);
    } else if (response.data.code == 404) {
        // Message({
        //     showClose: true,
        //     message: response.data.message,
        //     type: 'error'
        // });
        // router.js 已经判断每次进入路由,是否登陆了
        // 未登录会跳转到login,在附带原先path,所以下面可能无用!!!
        router.push({
            path: "/login"
        });
        return Promise.reject(response.data.message);
    } else if (response.data.code >= 400) {
        Message({
            showClose: true,
            message: response.data.message,
            type: 'error'
        });
        return Promise.reject(response.data.message);
    } else {
        return response;
    }
}, err => {
    if (err && err.response) {
        switch (err.response.status) {
            case 400:
                err.message = '请求错误(400)';
                break;
            case 401:
                err.message = '未授权,请重新登录(401)';
                break;
            case 403:
                err.message = '拒绝访问(403)';
                break;
            case 404:
                err.message = '请求出错(404)';
                break;
            case 408:
                err.message = '请求超时(408)';
                break;
            case 500:
                err.message = '服务器错误(500)';
                break;
            case 501:
                err.message = '服务未实现(501)';
                break;
            case 502:
                err.message = '网络错误(502)';
                break;
            case 503:
                err.message = '服务不可用(503)';
                break;
            case 504:
                err.message = '网络超时(504)';
                break;
            case 505:
                err.message = 'HTTP版本不受支持(505)';
                break;
            default:
                err.message = `连接出错(${err.response.status})!`;
        }
        Message.error({
            showClose: true,
            message: err.message,
            type: 'error'
        });
        return Promise.reject(err);
    }
})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77

3.3 源码展示

/*
 * @Author: Penk
 * @LastEditors: Penk
 * @LastEditTime: 2022-11-29 22:39:43
 * @FilePath: \front-master\src\myAxios\index.js
 * @email: 492934056@qq.com
 */
import axios from "axios";
import api_obj from "./api_obj";
import router from "../router";

// 实例化一个axios实例
let instance = axios.create({
  timeout: 60000,
});


function formData(temp_api) {
  let Http = {};
  for (const index in temp_api) {
    if (temp_api.hasOwnProperty(index)) {
      let apiTemp = temp_api[index];
      Http[index] = {};
      for (const key in apiTemp) {
        if (apiTemp.hasOwnProperty(key)) {
          let api = apiTemp[key]; // 获取method跟url
          Http[index][key] = function (
            params, // get/delete为urlparams,post/patch为data
            isFormData = false, // 默认是false,如果是post或者patch还要判断是否为formData
            config = {}
          ) {
            return new Promise(function (resolve, reject) {
              // window.console.log(key);
              let newParams = null;
              // 判断是否为formData数据
              if (isFormData && params) {
                let formData = new FormData();
                for (const key in params) {
                  if (params.hasOwnProperty(key)) {
                    formData.append(key, params[key]);
                    newParams = formData;
                  }
                }
              } else {
                newParams = params;
              }

              // 判断不同的请求,不同的接口
              if (api.method === "post" || api.method === "patch") {
                instance[api.method](api.url, newParams, config)
                  .then((res) => {
                    resolve(res);
                  })
                  .catch((err) => {
                    reject(err);
                  });
              } else if (api.method === "get" || api.method === "delete") {
                config.params = { ...params, ...api.params };
                instance[api.method](api.url, config)
                  .then((res) => {
                    resolve(res);
                  })
                  .catch((err) => {
                    reject(err);
                  });
              } else if (api.method === "put") {
                instance[api.method](api.url + params, config)
                  .then((res) => {
                    resolve(res);
                  })
                  .catch((err) => {
                    reject(err);
                  });
              } else if (api.method === "deleteID") {
                instance["delete"](api.url + "/" + params, config)
                  .then((res) => {
                    resolve(res);
                  })
                  .catch((err) => {
                    reject(err);
                  });
              }
            });
          };
        }
      }
    }
  }
  return Http;
}
var Https = {};
Https = formData(api_obj);

import { ElMessage } from "element-plus";

// 设置过滤器
instance.interceptors.request.use(
  (config) => {
    let userInfo = localStorage.getItem("userInfo");
    let token;
    if (!!userInfo && (token = JSON.parse(userInfo).token)) {
      // console.log("有token:", token);
      config.headers["token"] = "Penk " + token;
    }

    // 去除空字符串数据
    for (let key in config.data) {
      if (!config.data[key] && typeof config.data[key] == "String") {
        config.data[key] = null;
      }
    }
    return config;
  },
  (error) => {
    return Promise.reject(error);
  }
);

instance.interceptors.response.use(
  (response) => {
    if (!response.data || !response.data.code) {
      ElMessage({
        showClose: true,
        message: "后端返回格式不对!",
        type: "error",
      });
      return Promise.reject(response.data.message);
    } else if (response.data.code == 404) {
      ElMessage({
        showClose: true,
        message: response.data.message,
        type: "error",
      });
      // router.js 已经判断每次进入路由,是否登陆了
      // 未登录会跳转到login,在附带原先path,所以下面可能无用!!!
      router.push({
        path: "/login",
        query: {
          redirect: location.pathname
        }
      });
      return Promise.reject(response.data.message);
    } else if (response.data.code >= 400) {
      ElMessage({
        showClose: true,
        message: response.data.message,
        type: "error",
      });
      return Promise.reject(response.data.message);
    } else {
      return response.data.data;
    }
  },
  (err) => {
    if (err && err.response) {
      switch (err.response.status) {
        case 400:
          err.message = "请求错误(400)";
          break;
        case 401:
          err.message = "未授权,请重新登录(401)";
          break;
        case 403:
          err.message = "拒绝访问(403)";
          break;
        case 404:
          err.message = "请求出错(404)";
          break;
        case 408:
          err.message = "请求超时(408)";
          break;
        case 500:
          err.message = "服务器错误(500)";
          break;
        case 501:
          err.message = "服务未实现(501)";
          break;
        case 502:
          err.message = "网络错误(502)";
          break;
        case 503:
          err.message = "服务不可用(503)";
          break;
        case 504:
          err.message = "网络超时(504)";
          break;
        case 505:
          err.message = "HTTP版本不受支持(505)";
          break;
        default:
          err.message = `连接出错(${err.response.status})!`;
      }
      ElMessage.error({
        showClose: true,
        message: err.message,
        type: "error",
      });
      return Promise.reject(err);
    }
  }
);
export default {
  // 导出的是自定义的api
  main: Https,
  // 这个是通用的api,主要用其拦截器
  global: instance,
};

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/272513?site
推荐阅读
相关标签
  

闽ICP备14008679号