给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
相关推荐
William_Xu1 分钟前
JavaScript 并发控制
前端
拾年2752 分钟前
从零手写 Ajax:用原生 XHR 搭建前后端交互全流程
前端·javascript·ajax
光影少年3 分钟前
懒加载与分包:React.lazy + Suspense
前端·react.js·掘金·金石计划
小林ixn18 分钟前
你以为你懂 + 号?看完这篇 Bun + TS 实战,才发现以前全写错了
前端·javascript·typescript
namexingyun40 分钟前
开源前端生态如何成为 AI UI 生成的“燃料“:shadcn/ui、Tailwind CSS、Storybook 技术价值全解剖
java·前端·人工智能·python·ui·开源·ai编程
Zyed44 分钟前
[STM32]Day15读写FLASH+读取ID
前端·stm32·性能优化
jvxiao2 小时前
你真的懂作用域吗?从编译原理角度深度 JS 的作用域
前端·javascript
Darling噜啦啦2 小时前
二叉树与递归算法实战:从树结构到 LeetCode 爬楼梯,一文吃透前端数据结构与递归思维
前端·javascript·数据结构
星栈2 小时前
Rust + Makepad 应用怎么打包发布:Windows、macOS、Linux 全平台交付
前端·rust
Aolith2 小时前
React 路由守卫:我用一个组件替代了 Vue 的 beforeEach
前端·react.js