关于vue.js:Vue3-Ts-封装axios

8次阅读

共计 6088 个字符,预计需要花费 16 分钟才能阅读完成。

封装思路

index.ts
咱们须要在 src 的根目录下创立一个 axios 文件夹,其中创立一个 index.ts 文件,这个文件次要用来封装 axios 的配置(实例化申请配置、申请拦截器、相应拦截器)及相应的办法(登录跳转、音讯提醒、错误处理等)

base.ts
这个文件次要用于我的项目扩大的状况下 不同模块须要调用不同接口(申请的 base 地址 baseURL)而后期做的筹备,便于前期的保护

request.ts
次要用于封装基于 axios 配置的 get/post/put/delete 等应用办法。

api.ts
在前面的 main.ts 中引入该模块,包含所有接口数据信息写入该文件中。

index.ts

封装如下。思考到繁多职责,index 这块只封装 axios

// index.ts
import axios, {AxiosRequestConfig, Method} from "axios";
import router from "@/router";
import store from "@/store";
import {message} from 'ant-design-vue'
import {storage} from "../storage/storage";
import {dataList} from "@/components/aspin/data";

/** 
 * 跳转登录页
 * 携带以后页面路由,以期在登录页面实现登录后返回以后页面
 */
const toLogin = () => {
  router.replace({name: 'LoginPage',});
}

/** 
 * 申请失败后的谬误对立解决 
 * @param {Number} status 申请失败的状态码
 */
const errorHandle = (status: number, other: string) => {
  // 状态码判断
  switch (status) {case 302: message.error('接口重定向了!');
      break;
    case 400:
      message.error("收回的申请有谬误,服务器没有进行新建或批改数据的操作 ==>" + status)
      break;
    // 401: 未登录
    // 未登录则跳转登录页面,并携带以后页面的门路
    // 在登录胜利后返回以后页面,这一步须要在登录页操作。case 401: // 重定向
      message.error("token: 登录生效 ==>" + status + ":" + store.state.Roles)
      storage.remove(store.state.Roles)
      storage.get(store.state.Roles)
      router.replace({path: '/Login',});
      break;
    // 403 token 过期
    // 革除 token 并跳转登录页
    case 403:
      message.error("登录过期, 用户失去受权,然而拜访是被禁止的 ==>" + status)
      // store.commit('token', null);
      setTimeout(() => {
        router.replace({path: '/Login',});
      }, 1000);
      break;
    case 404:
      message.error("网络申请不存在 ==>" + status)
      break;
    case 406:
      message.error("申请的格局不可得 ==>" + status)
      break;
    case 408: message.error("申请超时!")
      break;
    case 410:
      message.error("申请的资源被永恒删除,且不会再失去的 ==>" + status)
      break;
    case 422:
      message.error("当创立一个对象时,产生一个验证谬误 ==>" + status)
      break;
    case 500:
      message.error("服务器产生谬误,请查看服务器 ==>" + status)
      break;
    case 502:
      message.error("网关谬误 ==>" + status)
      break;
    case 503:
      message.error("服务不可用,服务器临时过载或保护 ==>" + status)
      break;
    case 504:
      message.error("网关超时 ==>" + status)
      break;
    default:
      message.error("其余谬误谬误 ==>" + status)
  }
}

// 定义接口
interface PendingType {
  url?: string;
  method?: Method;
  params: any;
  data: any;
  cancel: any;
}
// 勾销反复申请
const pending: Array<PendingType> = [];
const CancelToken = axios.CancelToken;

// 移除反复申请
const removePending = (config: AxiosRequestConfig) => {for (const key in pending) {
    const item: number = +key;
    const list: PendingType = pending[key];
    // 以后申请在数组中存在时执行函数体
    if (list.url === config.url && list.method === config.method && JSON.stringify(list.params) === JSON.stringify(config.params) && JSON.stringify(list.data) === JSON.stringify(config.data)) {
      // 执行勾销操作
      list.cancel('操作太频繁,请稍后再试');
      // 从数组中移除记录
      pending.splice(item, 1);
    }
  }
};

/* 实例化申请配置 */
const instance = axios.create({
  headers: {
    //php 的 post 传输申请头肯定要这个 不然报错 接管不到值
    "Content-Type": "application/json;charset=UTF-8",
    "Access-Control-Allow-Origin-Type": '*'
  },
  // 申请时长
  timeout: 1000 * 30,
  // 申请的 base 地址 TODO: 这块当前依据不同的模块调不同的 api
  baseURL: process.env.VUE_APP_API_URL,
  //     ? "测试"
  //     : "正式",
  // 示意跨域申请时是否须要应用凭证
  withCredentials: false,
})

/** 
 * 申请拦截器 
 * 每次申请前,如果存在 token 则在申请头中携带 token 
 */
instance.interceptors.request.use(
  config => {removePending(config);
    config.cancelToken = new CancelToken((c) => {pending.push({ url: config.url, method: config.method, params: config.params, data: config.data, cancel: c});
    });
    // 登录流程管制中,依据本地是否存在 token 判断用户的登录状况        
    // 然而即便 token 存在,也有可能 token 是过期的,所以在每次的申请头中携带 token        
    // 后盾依据携带的 token 判断用户的登录状况,并返回给咱们对应的状态码        
    // 而后咱们能够在响应拦截器中,依据状态码进行一些对立的操作。// const token = store.state.token;
    // localStorage.setItem('token', token);

    if (storage.get(store.state.Roles)) {
      store.state.Roles
      config.headers.Authorization = storage.get(store.state.Roles);
    }
    return config;
  },
  error => {message.error(error.data.error.message);
    return Promise.reject(error.data.error.message);
  }

)

// 响应拦截器
instance.interceptors.response.use(function (config) {

  dataList.show = true
  removePending(config.config);
  // 申请胜利
  if (config.status === 200 || config.status === 204) {setTimeout(() => {dataList.show = false}, 400)
    return Promise.resolve(config);
  } else {return Promise.reject(config);
  }
  // 申请失败
}, function (error) {const { response} = error;
  if (response) {errorHandle(response.status, response.data.message);

    // 超时从新申请
    const config = error.config;
    // 全局的申请次数, 申请的间隙
    const [RETRY_COUNT, RETRY_DELAY] = [3, 1000];

    if (config && RETRY_COUNT) {
      // 设置用于跟踪重试计数的变量
      config.__retryCount = config.__retryCount || 0;
      // 查看是否曾经把重试的总数用完
      if (config.__retryCount >= RETRY_COUNT) {return Promise.reject(response || { message: error.message});
      }
      // 减少重试计数
      config.__retryCount++;
      // 发明新的 Promise 来解决指数后退
      const backoff = new Promise<void>((resolve) => {setTimeout(() => {resolve();
        }, RETRY_DELAY || 1);
      });
      // instance 重试申请的 Promise
      return backoff.then(() => {return instance(config);
      });
    }

    return Promise.reject(response);
  } else {
    // 解决断网的状况
    // eg: 申请超时或断网时,更新 state 的 network 状态
    // network 状态在 app.vue 中管制着一个全局的断网提醒组件的显示暗藏
    // 后续减少断网状况下做的一些操作
    store.commit('networkState', false);
  }
}
)
// 只须要思考繁多职责,这块只封装 axios
export default instance

base.ts

辨别每个模块的 baseUrl 不便前期保护治理

// base.ts
export class Base {
  /* 公共模块 */
  static env = process.env.NODE_ENV === "development"
    ? "http://localhost:8087"
    : "https://produceCommon.com(生产线地址)"
}

也能够间接在 index.ts 中设置这样就不须要 base.ts

const instance = axios.create({
  // 申请的 base 地址 TODO: 这块当前依据不同的模块调不同的 api
  baseURL: process.env.VUE_APP_API_URL,
})

须要配置根目录

.env.development

     NODE_ENV = 'development'
      # VUE_APP_API_URL = 'https://localhost:5001/'
    VUE_APP_API_URL = 'http://localhost:8087/'

.env.production

# 生产环境的申请接口
NODE_ENV = 'production'
VUE_APP_API_URL = 'http://129.xxxxx/'

request.ts

封装 axios 的 get、post 办法,其余对于接口调用的办法也可写入该文件中,便于管理。

// request.ts
import axios from "./index";
import qs from "qs";

export class Request {
  /**
   * get 办法
   * @param {string} url 门路
   * @param {object} params 参数
   */
  static get = (url: string, params?: any) => {return new Promise((resolve, reject) => {axios.get(url, { params: params}).then(res => {resolve(res);
      }).catch(err => {reject(err);
      })
    })
  }

  static post = (url: string, params?: any) => {return new Promise((resolve, reject) => {axios.post(url, qs.stringify(params)).then(res => {resolve(res);
      }).catch(err => {reject(err);
      })
    })
  }
}

api.ts

vue 页面须要应用的 api 接口

// 其中应用 install 的目标在于 ts 在 main.ts 中 
// 不能通过 Vue.prototype.$Api 这个形式间接调用
//,在全局办法中会说到应用 插件的形式去挂载。// api.ts
import {Base} from "./base";
import {Request} from "./request";

class api {
  /* api 接口模块 */
  public static article = {
     // 间接在 index.ts 中设置不须要 Base 模块
    genre: () => Request.get('/api/SnArticle/GetCountAsync'),
     // 基于 Base 模块封装调用
    genres: () => Request.get(`${Base.env}/api/SnArticle/GetCountAsync`),
  }
}
export {api}

index.vue

import {api} from '../../utils/api/api'
import {onMounted} from 'vue'
onMounted(async () => {await QueryAll()
  api.article.genre().then((res: any) => {console.log('genre' + res.data)
  })
  api.article.genres().then((res: any) => {console.log('genres' + res.data)
  })
})

参考

https://blog.csdn.net/qq_4003…

正文完
 0