给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
相关推荐
黄尚圈圈20 分钟前
Vue 中引入 ECharts 的详细步骤与示例
前端·vue.js·echarts
浮华似水1 小时前
简洁之道 - React Hook Form
前端
正小安3 小时前
如何在微信小程序中实现分包加载和预下载
前端·微信小程序·小程序
_.Switch5 小时前
Python Web 应用中的 API 网关集成与优化
开发语言·前端·后端·python·架构·log4j
一路向前的月光5 小时前
Vue2中的监听和计算属性的区别
前端·javascript·vue.js
长路 ㅤ   5 小时前
vite学习教程06、vite.config.js配置
前端·vite配置·端口设置·本地开发
长路 ㅤ   5 小时前
vue-live2d看板娘集成方案设计使用教程
前端·javascript·vue.js·live2d
Fan_web5 小时前
jQuery——事件委托
开发语言·前端·javascript·css·jquery
安冬的码畜日常5 小时前
【CSS in Depth 2 精译_044】第七章 响应式设计概述
前端·css·css3·html5·响应式设计·响应式
莹雨潇潇6 小时前
Docker 快速入门(Ubuntu版)
java·前端·docker·容器