谁用都说好!! 是时候摆脱接口文档了^▽^ Typescript 封装接口教程

68 阅读2分钟

作为一个前端开发, 免不了要调用各种各样的后端接口.
随着项目的复杂程度增长, 接口要传送的参数和返回的数据结构会变得越来越复杂.字段越堆越多, 嵌套一层又一层, 一不小心就会把我们给气死. 浏览器也会发出如下的叹息:

image.png

然后就又是一遍一遍的接口文档和代码比对着看,以期望找出其中的不同. 或者写了很多的 console.log 在控制台上苦苦寻觅......

不啰嗦了, 直接上代码!

基于axios + typescript的接口封装案例

首先创建两个文件

image.png

index.ts 基于axios封装 Api 类

// src/api/index.ts

import axios from 'axios'
import type {AxiosInstance, AxiosRequestConfig, AxiosResponse} from 'axios'

// 用于具体接口根据需要修改当次请求的axios配置
export type CustomConfig = {
  raw?: boolean
}

class Api {
  // axios 实例
  instance: AxiosInstance
  // 基础配置,url和超时时间
  baseConfig: AxiosRequestConfig = {baseURL: '/', timeout: 60000}

  constructor(config: AxiosRequestConfig) {
    // 创建axios实例
    this.instance = axios.create(Object.assign(this.baseConfig, config))

    this.instance.interceptors.request.use(
      (config: AxiosRequestConfig) => {
        // 请求拦截
        // 可以统一配置headers 如加token
        // config.headers!.Authorization = token

        return config
      },
      (err: any) => {
        return Promise.reject(err)
      }
    )

    this.instance.interceptors.response.use(
      (res: AxiosResponse & {config: CustomConfig}) => {
        // 响应拦截 可以在其中预处理返回数据
        return res.config.raw ? res.data : res.data.data
      },
      (err: any) => {
        // 这里用来处理http常见错误,进行全局提示
        let message = ''
        switch (err.response?.status) {
          case 400:
            message = '请求错误(400)'
            break
          default:
            message = `连接出错(${err.response.status})!`
        }
        // 这里是AxiosError类型,所以一般我们只reject我们需要的响应即可
        return Promise.reject(err.response)
      }
    )
  }
 
  // 定义请求方法
  public request(config: AxiosRequestConfig): Promise<AxiosResponse> {
    return this.instance.request(config)
  }
  //在这里 get 函数接受一个泛型参数 并返回这个类型对应的 Promise 泛型
  public get<T = any>(
    url: string,
    config?: AxiosRequestConfig & CustomConfig
  ): Promise<T> {
    return this.instance.get(url, config)
  }

  public post<T = any>(
    url: string,
    data?: any,
     config?: AxiosRequestConfig
  ): Promise<T> {
    return this.instance.post(url, data, config)
  }
}

export default Api

然后在 req.ts 中编写我们实际的接口

// src/api/req.ts

import type {AxiosRequestConfig} from 'axios'
import Api, {CustomConfig} from '@/api/index'

// 实例化接口类
export const api = new Api({
  // 这里填上我们接口的统一前缀
  baseURL: '/api/v1/'
})

// 看下面post函数的注释, 大同小异
export const get = <T = any, U = any>(url, _config?: CustomConfig) => {
  return function(params?: T, config?: AxiosRequestConfig) {
    return api.get<U>(url, {..._config, ...config, params})
  }
}

// 使用函数柯里化对每一个api连接都生成一个函数
// 这里的 post 函数是一个泛型函数
// T 表示请求参数的类型
// U 表示返回参数的类型 
// 这样只要我们在编写接口函数的时候写明类型, 在调用的时候就能直接获取类型提示了
export const post = <T = any, U = any>(url, _config?: CustomConfig) => {
  return function (data?: T, config?: AxiosRequestConfig) {
    return api.post<U>(url, data, {..._config, ...config})
  }
}

/////////////////////////////////
// 在这里可以声明我们具体的接口了
// 用来约束login接口的入参
type LoginParam = {username: string, password: string}
// 用来标明login接口的出参
type LoginRes = {token: string}
export const login = post<LoginParam, LoginRes>('permission/login')

看一下调用的效果吧😄

// 在某个异步函数中
const res = await login({username: '小明',  password: '123456'})

image.png

image.png 这不, 妥妥的提示出来了
最后, 欢迎点赞收藏啥的