ts封装axios最佳实践示例详解

作者:可畏 时间:2024-04-19 09:59:07 

简介

🤔看了一圈,大家对 ts 封装 axios 都各有见解。但都不是我满意的吧,所以自己封装了一个💪。至于为什么敢叫最佳实践,因为我满意,就这么简单粗暴🏋🏻。

什么样封装才是最合理的

别再用 promise 包了,好吗?

看了一下,很多人封装 axios 的时候都用 promise 包装了一层,甚至更有甚者用起了 try catch。为什么反对用 promise 包装,因为 axios 返回的就是个 promise ,脱裤子放屁,完全没必要🧔‍♀️。至于 try catch 这个是用于捕获未知错误的,比如 JSON.parse 的时候,有些字符串就是无法转换。记住一句话,滥用 try catch 和随地大小便没有区别。

一个 request 方法 * ,噗!我一口老血🥵

部分人直接就一个 request 方法 * ,所有参数与配置都写在一起,看起来一点也不清晰,简洁。请求有多种方式,getpostput...,最合理的请求方式应该是 instance[method](url, data, options)。对应 请求地址、请求参数、请求配置项,一目了然。

扩展我需要的请求,不要再 ts-ignore 了🤬

如果 ts-ignore 用多了,就会产生依赖性。不排除情况紧急急着上线,或者 类型处理 复杂的,但是在有时间的时候,还是得优化一下,作为程序员,追求优雅,永不过时。

求你了!把 * 拿出来吧😠

封装的时候我们都会封装一个请求类,但对应 * 应该解耦出来。因为每个域名的 * 处理可能不一致,写死的话封装请求类的意义也就没有了。

接口请求 then 里面又判断后端返回码判断请求是否成功,太狗血了!😞

🧑‍🏫看到下面这种代码,给我难受的啊。

api.post(url, data).then((res) => {
 if (res.code === 1) {
   // ...
 } else {
   // 全局消息提示
   console.error(res.message)
 }
})

既然是一个 promise ,我们就应该知道 promise 只有成功或者失败。then 怎么会有成功错误的处理呢?then 里面就是请求成功,没有什么 if else,处理失败去 catch 里面处理去。这么喜欢写 if else,你是没写过单元测试是吧?

开整

OK,吐槽了这么多,这时候肯定就有人说了,光说谁不会啊,你整一个啊!🤐

瞧你这话说的,一点活没干,还让你白嫖了。你咋这么能呢🙄?

不过话说回来,我不要活在他人的评价里,我做这件事情不是因为你的讽刺或者吹捧,而是我自己要做🧑‍🦱。

接下来定一下要做的事情

  • 封装一个请求类

  • 适当扩展 axios 类型,为自定义配置打地基

  • 支持自定义请求配置。如是否全局错误提示

  • * 单独拎出来,方便扩展新的请求

开整之前先看看 axios 基本类型


// 这是 axios 请求类型定义,但是因为我们需要支持自定义配置,所以待会需要把它拓展一下
export interface AxiosRequestConfig<D = any> {
 url?: string;
 method?: Method | string;
 baseURL?: string;
 transformRequest?: AxiosRequestTransformer | AxiosRequestTransformer[];
 transformResponse?: AxiosResponseTransformer | AxiosResponseTransformer[];
 headers?: (RawAxiosRequestHeaders & MethodsHeaders) | AxiosHeaders;
 params?: any;
 paramsSerializer?: ParamsSerializerOptions;
 data?: D;
 timeout?: Milliseconds;
 timeoutErrorMessage?: string;
 withCredentials?: boolean;
 // ...
}
// 这是 axios 请求返回类型定义,里面类型需要处理,所以这个我们也得处理一下。
export interface AxiosResponse<T = any, D = any> {
 data: T;
 status: number;
 statusText: string;
 headers: RawAxiosResponseHeaders | AxiosResponseHeaders;
 // 这里的配置没有支持拓展,所以待会也得处理一下
 config: InternalAxiosRequestConfig<D>;
 request?: any;
}
// 所以我们只需要改造 3 个 axios 类型定义就行了
// 另外我们需要定义下自己的 * 和 请求结果封装

Talk is cheap,show me the code.

代码也不多,就也不多解释了,基本注释都加上了。下面是全部代码。

import axios from 'axios'
import type { AxiosInstance, AxiosRequestConfig, AxiosResponse, InternalAxiosRequestConfig } from 'axios'
// 定义一个常见后端请求返回
type BaseApiResponse<T> = {
 code: number
 message: string
 result: T
}
// 拓展 axios 请求配置,加入我们自己的配置
interface RequestOptions {
 // 是否全局展示请求 错误信息
 globalErrorMessage?: boolean
 // 是否全局展示请求 成功信息
 globalSuccessMessage?: boolean
}
// 拓展自定义请求配置
interface ExpandAxiosRequestConfig<D = any> extends AxiosRequestConfig<D> {
 interceptorHooks?: InterceptorHooks
 requestOptions?: RequestOptions
}
// 拓展 axios 请求配置
interface ExpandInternalAxiosRequestConfig<D = any> extends InternalAxiosRequestConfig<D> {
 interceptorHooks?: InterceptorHooks
 requestOptions?: RequestOptions
}
// 拓展 axios 返回配置
interface ExpandAxiosResponse<T = any, D = any> extends AxiosResponse<T, D> {
 config: ExpandInternalAxiosRequestConfig<D>
}
export interface InterceptorHooks {
 requestInterceptor?: (config: InternalAxiosRequestConfig) => InternalAxiosRequestConfig
 requestInterceptorCatch?: (error: any) => any
 responseInterceptor?: (response: AxiosResponse) => AxiosResponse | Promise<AxiosResponse>
 responseInterceptorCatch?: (error: any) => any
}
// 导出Request类,可以用来自定义传递配置来创建实例
export default class Request {
 // axios 实例
 private _instance: AxiosInstance
 // 默认配置
 private _defaultConfig: ExpandAxiosRequestConfig = {
   baseURL: '/api',
   timeout: 5000,
   requestOptions: {
     globalErrorMessage: true,
     globalSuccessMessage: false
   }
 }
 private _interceptorHooks?: InterceptorHooks
 constructor(config: ExpandAxiosRequestConfig) {
   // 使用axios.create创建axios实例
   this._instance = axios.create(Object.assign(this._defaultConfig, config))
   this._interceptorHooks = config.interceptorHooks
   this.setupInterceptors()
 }
 // 通用拦截,在初始化时就进行注册和运行,对基础属性进行处理
 private setupInterceptors() {
   this._instance.interceptors.request.use(this._interceptorHooks?.requestInterceptor, this._interceptorHooks?.requestInterceptorCatch)
   this._instance.interceptors.response.use(this._interceptorHooks?.responseInterceptor, this._interceptorHooks?.responseInterceptorCatch)
 }
 // 定义核心请求
 public request(config: ExpandAxiosRequestConfig): Promise<AxiosResponse> {
   // !!!⚠️ 注意:axios 已经将请求使用 promise 封装过了
   // 这里直接返回,不需要我们再使用 promise 封装一层
   return this._instance.request(config)
 }
 public get<T = any>(url: string, config?: ExpandAxiosRequestConfig): Promise<AxiosResponse<BaseApiResponse<T>>> {
   return this._instance.get(url, config)
 }
 public post<T = any>(url: string, data?: any, config?: ExpandAxiosRequestConfig): Promise<T> {
   return this._instance.post(url, data, config)
 }
 public put<T = any>(url: string, data?: any, config?: ExpandAxiosRequestConfig): Promise<T> {
   return this._instance.put(url, data, config)
 }
 public delete<T = any>(url: string, config?: ExpandAxiosRequestConfig): Promise<T> {
   return this._instance.delete(url, config)
 }
}

以及使用的 demo。这个保姆级服务满意吗?

// 请求 *
const transform: InterceptorHooks = {
 requestInterceptor(config) {
   // 请求头部处理,如添加 token
   const token = 'token-value'
   if (token) {
     config!.headers!.Authorization = token
   }
   return config
 },
 requestInterceptorCatch(err) {
   // 请求错误,这里可以用全局提示框进行提示
   return Promise.reject(err)
 },
 responseInterceptor(result) {
   // 因为 axios 返回不支持扩展自定义配置,需要自己断言一下
   const res = result as ExpandAxiosResponse
   // 与后端约定的请求成功码
   const SUCCESS_CODE = 1
   if (res.status !== 200) return Promise.reject(res)
   if (res.data.code !== SUCCESS_CODE) {
     if (res.config.requestOptions?.globalErrorMessage) {
       // 这里全局提示错误
       console.error(res.data.message)
     }
     return Promise.reject(res.data)
   }
   if (res.config.requestOptions?.globalSuccessMessage) {
     // 这里全局提示请求成功
     console.log(res.data.message)
   }
   // 请求返回值,建议将 返回值 进行解构
   return res.data.result
 },
 responseInterceptorCatch(err) {
   // 这里用来处理 http 常见错误,进行全局提示
   const mapErrorStatus = new Map([
     [400, '请求方式错误'],
     [401, '请重新登录'],
     [403, '拒绝访问'],
     [404, '请求地址有误'],
     [500, '服务器出错']
   ])
   const message = mapErrorStatus.get(err.response.status) || '请求出错,请稍后再试'
   // 此处全局报错
   console.error(message)
   return Promise.reject(err.response)
 }
}
// 具体使用时先实例一个请求对象
const request = new Request({
 baseURL: '/api',
 timeout: 5000,
 interceptorHooks: transform
})
// 定义请求返回
interface ResModel {
 str: string
 num: number
}
// 发起请求
request
 .post<ResModel>(
   '/abc',
   {
     a: 'aa',
     b: 'bb'
   },
   {
     requestOptions: {
       globalErrorMessage: true
     }
   }
 )
 .then((res) => {
   console.log('res: ', res)
   console.log(res.str)
 })

可以看到鼠标浮上去就能看到定义了,完美!

ts封装axios最佳实践示例详解

最后源码地址: github.com/coveychen95&hellip;

来源:https://juejin.cn/post/7208465670992478269

标签:ts,axios,封装
0
投稿

猜你喜欢

  • golang时间、时区、格式的使用方法

    2024-02-02 07:46:06
  • GOLang单元测试用法详解

    2024-05-05 09:27:33
  • 运行asp.net程序 报错:磁盘空间不足

    2011-11-03 17:16:22
  • Mysql 本地计算机无法启动 mysql 服务 错误 1067:进程意外终止。

    2024-01-27 19:30:56
  • Python实现邮件发送功能的示例详解

    2022-08-18 13:15:43
  • FrontPage XP设计教程2——网页的编辑

    2008-10-11 12:16:00
  • oracle日志操作模式(归档模式和非归档模式的利与弊)

    2024-01-24 18:01:36
  • 浅谈django model postgres的json字段编码问题

    2021-12-21 13:55:12
  • 利用Python绘制虎年烟花秀

    2022-10-08 06:03:49
  • Python之列表的append()方法最容易踩的坑

    2022-05-14 18:56:53
  • pandas按行按列遍历Dataframe的几种方式

    2023-07-04 15:36:05
  • 支持在线写SQL的Oracle学习免费网站推荐!(个人常使用)

    2023-07-24 09:06:51
  • 在opera里css出现渲染问题

    2009-01-15 12:19:00
  • 快速解决pandas.read_csv()乱码的问题

    2023-07-10 21:14:47
  • Python编程之变量赋值操作实例分析

    2021-09-20 12:12:01
  • PHP实现简单注册登录系统

    2024-05-02 17:34:13
  • Mysql8.0.22解压版安装教程(小白专用)

    2024-01-15 15:37:29
  • MySQL 8.0.20 Window10免安装版配置及Navicat管理教程图文详解

    2024-01-24 02:00:38
  • PHP strip_tags() 去字符串中的 HTML、XML 以及 PHP 标签的函数

    2023-06-09 01:05:00
  • vue使用百度地图报错BMap is not defined问题及解决

    2024-04-26 17:42:02
  • asp之家 网络编程 m.aspxhome.com