封装思路

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.tsimport 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);  }})// 只须要思考繁多职责,这块只封装axiosexport default instance

base.ts

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

// base.tsexport 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.tsimport 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.tsimport { 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...