typeScript--6实战封装axios

typeScript--6实战封装axios

在vue3 + typeScript 项目中配置axios

项目创建

  1. 使用npm 创建项目
npm create vite@latest

![过程选择](enter image description here
完成上面的操作, 就把一个简单的项目创建了,接下来用一个常用的功能来看下 typeScript 是怎么使用的

给项目配置axios 来属性下typeScript 的写法

1.在开始之前,先看下 axios 提供的类的申明文件,对他有个了解, 这是用一个新的插件首先要看的。 目录为 /node_modules/axios/index.t.ts

  1. 下面 我们看下 Axios
export class Axios {
  constructor(config?: AxiosRequestConfig);
  defaults: AxiosDefaults;
  interceptors: {
    request: AxiosInterceptorManager<AxiosRequestConfig>;
    response: AxiosInterceptorManager<AxiosResponse>;
  };
  getUri(config?: AxiosRequestConfig): string;
  request<T = any, R = AxiosResponse<T>, D = any>(config: AxiosRequestConfig<D>): Promise<R>;
  get<T = any, R = AxiosResponse<T>, D = any>(url: string, config?: AxiosRequestConfig<D>): Promise<R>;
  delete<T = any, R = AxiosResponse<T>, D = any>(url: string, config?: AxiosRequestConfig<D>): Promise<R>;
  head<T = any, R = AxiosResponse<T>, D = any>(url: string, config?: AxiosRequestConfig<D>): Promise<R>;
  options<T = any, R = AxiosResponse<T>, D = any>(url: string, config?: AxiosRequestConfig<D>): Promise<R>;
  post<T = any, R = AxiosResponse<T>, D = any>(url: string, data?: D, config?: AxiosRequestConfig<D>): Promise<R>;
  put<T = any, R = AxiosResponse<T>, D = any>(url: string, data?: D, config?: AxiosRequestConfig<D>): Promise<R>;
  patch<T = any, R = AxiosResponse<T>, D = any>(url: string, data?: D, config?: AxiosRequestConfig<D>): Promise<R>;
  postForm<T = any, R = AxiosResponse<T>, D = any>(url: string, data?: D, config?: AxiosRequestConfig<D>): Promise<R>;
  putForm<T = any, R = AxiosResponse<T>, D = any>(url: string, data?: D, config?: AxiosRequestConfig<D>): Promise<R>;
  patchForm<T = any, R = AxiosResponse<T>, D = any>(url: string, data?: D, config?: AxiosRequestConfig<D>): Promise<R>;
}

这里定义的就是 axios 的 类的类型
里面比较熟悉常用的 request, get, post, put, delete, head
在这里 我们看下 request 应为其他几个就是对request 的封装。

request<T = any, R = AxiosResponse<T>, D = any>(config: AxiosRequestConfig<D>): Promise<R>;

request 方法有三个泛型,T ,R 和 D,接收一个 AxiosRequestConfig 类型的参数作为配置对象,返回值的类型是一个接收泛型R 的 Promise 类型。

这三个泛型,T 的含义是什么?要去看 R。R 的含义是什么?要去看它的默认类型 AxiosResponse

export interface AxiosResponse<T = any, D = any>  {
  data: T;
  status: number;
  statusText: string;
  headers: RawAxiosResponseHeaders | AxiosResponseHeaders;
  config: AxiosRequestConfig<D>;
  request?: any;
}

看到这个结构,我们知道了,原来 AxiosResponse 就是在设置响应拦截器中用到的那个 response 对象的类型。

同时也就知道了泛型 T 就是服务器返回的数据的类型。因为服务器究竟返回什么类型,代码是不知道的,所以它的默认类型是 any。

回到头来再来看 request 方法的定义,现在就能明白了,它接收的第一个泛型 T 就是将来服务器返回数据的类型,R 就是这个数据经过 axios 包装一层得到的 response 对象的类型,而 request 方法的返回值是一个 Promise,其值就是成功态的 R,也就是 response对象。

  1. 下面再通过 AxiosRequestConfig 看下泛型 D 是什么
export interface AxiosRequestConfig<D = any> {
  url?: string;
  method?: Method | string;
  baseURL?: string;
  transformRequest?: AxiosRequestTransformer | AxiosRequestTransformer[];
  transformResponse?: AxiosResponseTransformer | AxiosResponseTransformer[];
  headers?: RawAxiosRequestHeaders;
  params?: any;
  paramsSerializer?: ParamsSerializerOptions;
  data?: D;
....
}

相信大家能看出来,它其实就是 axios 的配置对象的类型。在设置请求拦截器和封装公共请求方法时会用到。

  1. AxiosInstance
    该类型为 axios.create 方法创建出的实例的类型。后面直接用到。
  2. AxiosError
    该类型为请求发送过程中出现错误产生的错误对象的类型:
    我们会用到其中的 response 属性,它表示响应对象,需要根据它的 HTTP 状态码做一些处理。

开始封装 axios

新建一个 src/utils/request.ts 文件,在这个文件中对 Axios 进行封装。

  1. Result 类型
    从服务器返回的数据类型 一般张这个样子
{
  code: 0,
  message: '',
  data: {}
}

定义一个接口 Result 来表示它,其中 data 的类型不确定,所以就要用到泛型了:

/* 服务器返回数据的的类型,根据接口文档确定 */
export interface Result<T=any> {
  code: number,
  message: string,
  data: T
}

这是一个泛型接口。和泛型函数差不多,在定义时声明一个泛型,用这个泛型去规范成员的类型。将来使用该接口的时候,再去确定泛型的具体类型。
2. 创建实例

import axios from 'axios'
import type { AxiosInstance } from 'axios'

const service: AxiosInstance = axios.create({
  baseURL: '/api',
  timeout: 30000
})
  1. 请求拦截器
import type { AxiosError, AxiosRequestConfig } from 'axios'
import { message as Message } from 'ant-design-vue'

/* 请求拦截器 */
service.interceptors.request.use((config: AxiosRequestConfig) => {
  //  伪代码
  // if (token) {
  //   config.headers.Authorization = `Bearer ${token}`;
  // }
  return config
}, (error: AxiosError) => {
  Message.error(error.message);
  return Promise.reject(error)
})
  1. 响应拦截器
    响应拦截器大家也很熟悉了。简单说一下做了哪些事情:
  • 根据自定义错误码判断请求是否成功,然后只将组件会用到的数据,也就是上面的 Result 中的 data 返回
  • 如果错误码判断请求失败,此时为业务错误,比如用户名不存在等,在这里进行提示
  • 如果网络错误,则进入第二个回调函数中,根据不同的状态码设置不同的提示消息进行提示
import type { AxiosError, AxiosResponse } from 'axios'
import { message as Message } from 'ant-design-vue'

/* 响应拦截器 */
service.interceptors.response.use((response: AxiosResponse) => {
  const { code, message, data } = response.data

  // 根据自定义错误码判断请求是否成功
  if (code === 0) {
    // 将组件用的数据返回
    return data
  } else {
    // 处理业务错误。
    Message.error(message)
    return Promise.reject(new Error(message))
  }
}, (error: AxiosError) => {
  // 处理 HTTP 网络错误
  let message = ''
  // HTTP 状态码
  const status = error.response?.status
  switch (status) {
    case 401:
      message = 'token 失效,请重新登录'
      // 这里可以触发退出的 action
      break;
    case 403:
      message = '拒绝访问'
      break;
    case 404:
      message = '请求地址错误'
      break;
    case 500:
      message = '服务器故障'
      break;
    default:
      message = '网络连接故障'
  }
  
  Message.error(message) 
  return Promise.reject(error)
})
  1. 公共请求方法
    这里是本文的重点,也是 TS 封装 Axios 的重点。使用 TS 无非要获得良好的代码提示,我们在调用接口时编辑器能提示出该接口的返回值有哪些。
export const http = {
  get<T=any>(url: string, config?: AxiosRequestConfig) : Promise<T> {
    return service.get(url, config)
  },

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

  put<T=any>(url: string, data?: object, config?: AxiosRequestConfig) :Promise<T> {
    return service.put(url, data, config)
  },

  delete<T=any>(url: string, config?: AxiosRequestConfig) : Promise<T> {
    return service.delete(url, config)
  }
}

我们以 get 方法为例,看下为什么这么封装?
上文说过,axios 的 requet,get 这些方法,返回的都是一个 AxiosResponse 类型的数据。
默认的响应拦截器返回的是完整的 response 对象,类型为 AxiosResponse 。

经过我们的修改,现在响应拦截器只返回 response.data.data,对应的类型就是
AxiosResponse 中的 T。所以这里的 service.get 方法,其实际返回类型应为 T,但是编辑器并不知道

所以我们要手动帮助编辑器“修正”类型提示,也就是不依靠 TS 的类型推导,而是主动设置返回类型为 Promise :

这里的泛型 T 表示的服务器返回数据的类型,具体返回什么类型,要到接口调用时才知道。所以下面来到 API 层,去封装请求接口。

之所以封装这一层公共请求方法,主要目的就是为了帮编译器正确识别类型。方法就是手动设置返回类型。如果直接使用 axios 实例的请求方法,就需要在每次调用时都指定一遍,当接口有几十上百个时,多少会在增加一些工作量。所以就在这里多加一层,提前将类型制定好。

API层

准备两个文件,一个写类型,一个写接口。
api

在 api/user/types.ts 文件中,写接口需要的参数的类型,和接口返回数据的类型。这里的类型,要根据接口文档而定。

/* 登录接口参数类型 */
export interface LoginData {
  username: string,
  password: string,
}

/* 登录接口返回值类型 */
export interface LoginRes {
  token: string
}

/* 用户信息接口返回值类型 */
export interface UserInfoRes {
  id: string,
  username: string,
  avatar: string,
  description: string,
}

在 api/user/index.ts 文件中,封装组件用到的接口。

import request, { http } from '@/utils/request'

import type { LoginData, LoginRes, UserInfoRes} from './types'

/**
 * 登录
 */
export function login(data: LoginData) {
  return http.post<LoginRes>('/user/login', data);
}

/**
 * 获取登录用户信息
 */
export function getUserInfo() {
  return http.get<UserInfoRes>('/user/info')
}

再这里使用的时候 调用 login 接口, 定义了

http.post<loginRes>

通过传入响应参数的类型,现在 Axios 经过 TypeScript 的封装,可以给出友好的类型提示,这对于开发体验和效率都有很好的提升。

下面是我的小程序体验码,希望能和大家共同学习进步

posted @ 2022-11-25 15:11  eyes-star  阅读(834)  评论(0编辑  收藏  举报