javascript技巧

关注公众号 jb51net

关闭
首页 > 网络编程 > JavaScript > javascript技巧 > ts封装axios

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

作者:可畏

这篇文章主要为大家介绍了ts封装axios最佳实践示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

简介

🤔看了一圈,大家对 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 请求类型定义,但是因为我们需要支持自定义配置,所以待会需要把它拓展一下
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)
  })

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

最后源码地址: github.com/coveychen95…

以上就是ts封装axios最佳实践示例详解的详细内容,更多关于ts封装axios的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文