给axios附上体操魔法

本篇章是利用TS的特性去封装axios从而获得魔法般的语法提示。

注意:本篇需要一定的TypeScript基础。

首先我们定义一个Axios类,并且声明私有的axios实例变量,并且在构造函数内赋值给他axios实例,axiosConfig是axios的基本配置参数,实例化Axios时可传过来作为默认配置,如下面代码:

typescript 复制代码
import axios, { AxiosInstance, AxiosRequestConfig } from "axios";

export interface CreateAxiosConfig extends AxiosRequestConfig {
  /* 这里定义传递参数类型 */
  [key: string]: any // 不确定的话就用这个
}

class Axios<T, TT> {
  private axiosInstance: AxiosInstance

  constructor(config: CreateAxiosConfig) {
    this.axiosInstance = axios.create(config)
  }
}

注:CreateAxiosConfig类型继承了原有的请求头类型并且可以自定义请求头传递参数。

紧接着我们新增两个方法,分别叫 interceptorsRequest、interceptorsResponse,他们分别是请求拦截处理方法和响应拦截处理方法,如下面代码:

typescript 复制代码
private interceptorsRequest() {
    this.axiosInstance.interceptors.request
      .use(
        (config: CreateAxiosConfig) => {
          /* 请求拦截处理逻辑 ----- 例子 */
          
          config.headers!['Content-Type'] = 'application/json;charset=UTF-8'

          return config
        },

        (error: Error) => {

          return Promise.reject(error)
        }
      )
  }
  
private interceptorsResponse() {
    this.axiosInstance.interceptors.response
      .use(
        (response: AxiosResponse) => {
          /* 响应拦截处理逻辑 */
          return response
        },
        (error: Error) => {

          return Promise.reject(error)
        }
      )
  }

注意: 这两方法需要在构造器上初始化调用一下。

紧接着我们新增一个对外暴露的初始化方法,代码如下:

typescript 复制代码
// TTT为request本身泛型,T,TT则为我们定义的Axios,具体约定为T请求类型,TT为响应类型

request<TTT>(options: T): Promise<TTT> {
    return new Promise((resolve, reject) => {
    
      this.axiosInstance.request<TT>(options)
        .then((res: AxiosResponse<TT>) => {
        
          resolve((res as any) as Promise<TTT>) // 响应返回Promise
        }).catch((e: Error) => {
          reject(e)
        })
    })
  }

首先我们看Axios内部的request方法类型,发现他接受三个泛型,第二个为返回类型,但是依赖第一个类型

我们接着看内部的AxiosResponse类型,也发现他接收两个泛型,但是第一个泛型则为响应返回结果的类型,因此,我们只需要定义我们返回类型即可。

OK,接下来我们暴露一个函数,作为实例化我们Axios类,如下代码:

typescript 复制代码
export interface HeaderResponse { // 自定义响应头类型,可根据自己实际更换
  code: number,
  message?: String,
  data?: any,
  [key: string]: any
}

export interface HeaderRequest { // 自定义请求头类型
  url: string,
  method?: 'post' | 'get' | 'put',
  params?: any,
  data?: Record<string, any>,
  [key: string]: any
}

export type AxiosReturnData<T> = Omit<AxiosResponse, 'data'> & {  // 响应结果类型  ---- 可根据自己实际字段更换
  data: {
    code: string | number,
    content: T,
    message: string
  }
} 

function request<T>(options: HeaderRequest): Promise<AxiosReturnData<T>> {
  if(!options.method) {
    options.method = 'get'
  }
  return new Axios<HeaderRequest, HeaderResponse>( // 返回初始化的Axios实例
    {
      timeout: 30000,
      withCredentials: true,
      baseURL: '/api',
      headers: { 'Cache-Control': 'no-cache' }
    }
  ).request<AxiosReturnData<T>>(options)
}

在构造axios实例的时候我们需要定义请求类型和响应类型,泛型参数需要自行定义,以便我们在使用时可以根据不同的请求和响应类型去实例化不同的axios实例。

试验:

typescript 复制代码
import { request } from "./request"

export const login = <T>(data) => {
  return request<T>({
    url: `/login`,
    method: 'post',
    data
  })
}

login1<{token: string}>({ // 调用
  userCode: 'test',
  verCode: '111'
})
.then(res => {
  console.log(res.data.content.token)
})

这就拥有很良好的代码提示啦

整体源码

typescript 复制代码
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from "axios";

export interface CreateAxiosConfig extends AxiosRequestConfig {
  [key: string]: any
}

export interface HeaderResponse {
  code: number,
  message?: String,
  data?: any,
  [key: string]: any
}

export interface HeaderRequest {
  url: string,
  method?: 'post' | 'get' | 'put',
  params?: any,
  data?: Record<string, any>,
  [key: string]: any
}

export type AxiosReturnData<T> = Omit<AxiosResponse, 'data'> & {
  data: {
    code: string | number,
    content: T,
    message: string
  }
} 


class Axios<T, TT> {
  private axiosInstance: AxiosInstance

  constructor(config: CreateAxiosConfig) {
    this.axiosInstance = axios.create(config)

    this.initAxios()
  }

  private initAxios() {
    this.interceptorsRequest()
    this.interceptorsResponse()
  }

  private interceptorsRequest() {
    this.axiosInstance.interceptors.request
      .use(
        (config: CreateAxiosConfig) => {
          config.headers!['Content-Type'] = 'application/json;charset=UTF-8'

          return config
        },

        (error: Error) => {

          return Promise.reject(error)
        }
      )
  }

  private interceptorsResponse() {
    this.axiosInstance.interceptors.response
      .use(
        (response: AxiosResponse) => {
          
          return response
        },
        (error: Error) => {

          
          return Promise.reject(error)
        }
      )
  }

  request<TTT>(options: T): Promise<TTT> {
    return new Promise((resolve, reject) => {

      this.axiosInstance.request<TT>(options)
        .then((res: AxiosResponse<TT>) => {
          
          resolve((res as any) as Promise<TTT>)
        }).catch((e: Error) => {
          reject(e)
        })
    })
  }
}

function request<T>(options: HeaderRequest): Promise<AxiosReturnData<T>> {
  if(!options.method) {
    options.method = 'get'
  }
  return new Axios<HeaderRequest, HeaderResponse>(
    {
      timeout: 30000,
      withCredentials: true,
      baseURL: '/api',
      headers: { 'Cache-Control': 'no-cache' }
    }
  ).request<AxiosReturnData<T>>(options)
}

export default request
相关推荐
search76 小时前
前端设计:CRG 3--CDC error
前端
治金的blog6 小时前
vben-admin和vite,ant-design-vue的结合的联系
前端·vscode
利刃大大7 小时前
【Vue】Vue2 和 Vue3 的区别
前端·javascript·vue.js
荔枝一杯酸牛奶8 小时前
HTML 表单与表格布局实战:两个经典作业案例详解
前端·html
Charlie_lll9 小时前
学习Three.js–纹理贴图(Texture)
前端·three.js
yuguo.im9 小时前
我开源了一个 GrapesJS 插件
前端·javascript·开源·grapesjs
安且惜9 小时前
带弹窗的页面--以表格形式展示
前端·javascript·vue.js
摘星编程10 小时前
React Native鸿蒙:BiometricAuth指纹解锁实现
react native·react.js·harmonyos
摘星编程10 小时前
用React Native开发OpenHarmony应用:NFC读取标签数据
javascript·react native·react.js
GISer_Jing10 小时前
AI驱动营销:业务技术栈实战(From AIGC,待总结)
前端·人工智能·aigc·reactjs