通过TypeScript封装axios

简介: 通过TypeScript封装axios

划分代码文件结构


  • config: 导出一些全局变量


  • type: 定义一个类型接口


  • index: axios的封装文件


config文件


// 配置请求超时时间,
    // 根据不同环境来改变baseUrl
    let BASE_URL = ''
    const TIME_OUT = 5000
    if (process.env.NODE_ENV === 'development') {
      BASE_URL = 'development'
    } else if (process.env.NODE_ENV === 'production') {
      BASE_URL = 'production'
    } else {
      BASE_URL = 'test'
    }
    export { BASE_URL, TIME_OUT }


type文件


如果我们不封装axios,我们直接使用axios提供的类型即可。但是我们需要提供一个拦截器函数,并且还需要展示loading,所以我们需要扩展一些内置的接口。


import type { AxiosRequestConfig, AxiosResponse } from 'axios'
    // 定义传入的拦截器接口,并且都是可以可选的。
    interface IRequestInterceptors<T = AxiosResponse> {
      // 请求成功时的拦截器
      requestSuccessInterceptor?: (config: AxiosRequestConfig) => AxiosRequestConfig
      // 请求失败时的拦截器
      requestErrorInterceptor?: (err: any) => any
      // 响应成功时的拦截器
      responseSuccessInterceptor?: (res: T) => T
      // 响应失败时的拦截器
      responseErrorInterceptor?: (err: any) => any
    }
    // 这个接口将要代替AxiosRequestConfig
    export interface IRequestConfig<T = AxiosResponse> extends AxiosRequestConfig {
      // 每个request实例可以不传入拦截器
      interceptors?: IRequestInterceptors<T>
      // 是否显示loading
      showLoading?: boolean
    }


index文件


我们通过类来对axios封装,使其可以创建多个axios实例。有自己独特的状态。


定义axios的全局拦截器


他的目的是,每个axios实例和请求都需要做的操作,我们可以放在全局拦截器中。此时的拦截器我们在构造函数中定义其逻辑即可。


import axios, { AxiosInstance } from 'axios'
    import { IRequestConfig } from './type'
    class Request {
      public instance: AxiosInstance
      constructor(config: IRequestConfig) {
        this.instance = axios.create(config)
        // 创建全局请求拦截器
        this.instance.interceptors.request.use(
          (config) => {
            console.log('全局请求成功创建的拦截器')
            return config
          },
          (err) => {
            console.log('全局请求失败创建的拦截器')
            return err
          }
        )
        // 创建全局响应拦截器
        this.instance.interceptors.response.use(
          (config) => {
            console.log('全局响应成功创建的拦截器')
            return config
          },
          (err) => {
            console.log('全局响应失败创建的拦截器')
            return err
          }
        )
      }
    }


定义axios的实例拦截器


这些拦截器其实和全局一样,如果没有创建多个axios实例的时候。大部分情况下,我们是不需要创建多个axios实例的。这时候我们扩展的AxiosRequestConfig接口就派上用场了。这些拦截器我们就需要在创建实例的时候传入对应的逻辑。然后再构造函数中注册即可.


import axios, { AxiosInstance } from 'axios'
    import { IRequestConfig } from './type'
    class Request {
      public instance: AxiosInstance
      constructor(config: IRequestConfig) {
        this.instance = axios.create(config)
        // 创建实例请求拦截器
        this.instance.interceptors.request.use(
          config.interceptors?.requestSuccessInterceptor,
          config.interceptors?.requestErrorInterceptor
        )
        // 创建实例请求拦截器
        this.instance.interceptors.response.use(
          config.interceptors?.responseSuccessInterceptor,
          config.interceptors?.responseErrorInterceptor
        )
      }
    }


封装request函数


其实这个就是调用axios中的 request 函数做二次封装。


我们知道promise的优势,所以我们将request返回一个promise对象。但是promise需要传入一个泛型,作为返回值的类型。所以我们调用request函数的时候需要传入一个返回值的类型。并且我们可以在这里创建单个请求独有的拦截器。这些拦截器需要在调用request函数的时候传入对应的逻辑。并且调用传入的拦截器,并将其返回值给配置对象或者返回值。


// 传入的泛型是约束返回值
  request<T>(config: IRequestConfig<T>): Promise<T> {
    return new Promise((resolve, reject) => {
      // 创建单个请求的请求拦截器
      if (config.interceptors?.requestSuccessInterceptor) {
        // 直接调用,然后将处理后的config返回
        config = config.interceptors.requestSuccessInterceptor(config)
      }
      this.instance
        .request<any, T>(config)
        .then((res) => {
          // 调用传入的响应拦截器
          if (config.interceptors?.responseSuccessInterceptor) {
            res = config.interceptors.responseSuccessInterceptor(res)
          }
          resolve(res)
        })
        .catch((err) => {
          reject(err)
        })
    })
  }


封装其他的请求方法。


这些方法都是借助上面封装的request方法。


get<T>(config: IRequestConfig<T>): Promise<T> {
        return this.request<T>({ ...config, method: 'GET' })
      }
      post<T>(config: IRequestConfig<T>): Promise<T> {
        return this.request<T>({ ...config, method: 'POST' })
      }
      delete<T>(config: IRequestConfig<T>): Promise<T> {
        return this.request<T>({ ...config, method: 'DELETE' })
      }
      patch<T>(config: IRequestConfig<T>): Promise<T> {
        return this.request<T>({ ...config, method: 'PATCH' })
      }


通过element-plus来加载loading


loading组件,我们可以通过element-plus中的el-loading来加载。并且以服务的方式进行调用。注意新版本的类型导入路径。


import { ILoadingInstance } from 'element-plus/lib/components/loading'
    import axios, { AxiosInstance } from 'axios'
    import { IRequestConfig } from './type'
    import { ElLoading } from 'element-plus/lib/components'
    // 默认加载loading
    const DEFAULT_LOADING = true
    // 定义两个属性
    public showLoading: boolean
    public loadingInstance?: ILoadingInstance
    constructor(config: IRequestConfig) {
        // 默认不加载loading
        this.showLoading = config.showLoading ?? DEFAULT_LOADING
        this.instance = axios.create(config)
        // 创建全局请求拦截器
        this.instance.interceptors.request.use(
          (config) => {
            console.log('全局请求成功创建的拦截器')
            // 当showLoading为true是加载loading
            if (this.showLoading) {
              // 添加加载loading
              this.loadingInstance = ElLoading.service({
                text: '正在加载,请稍等...',
                background: 'rgba(0, 0, 0, .1)',
                lock: true
              })
            }
            return config
          },
          (err) => {
            console.log('全局请求失败创建的拦截器')
            // 请求错误,让loading关闭
            this.loadingInstance?.close()
            return err
          }
        )
         // 创建全局响应拦截器
        this.instance.interceptors.response.use(
          (config) => {
            console.log('全局响应成功创建的拦截器')
           // 响应时,让loading关闭
            this.loadingInstance?.close()
            return config
          },
          (err) => {
            console.log('全局响应失败创建的拦截器')
            // 响应出错时,让loading关闭
            this.loadingInstance?.close()
            return err
          }
        )
      }
  // 传入的泛型是约束返回值
  request<T>(config: IRequestConfig<T>): Promise<T> {
    return new Promise((resolve, reject) => {
      // 定制该请求是否加loading。当为传入该参数时,默认为true
      if (config.showLoading === false) {
        this.showLoading = false
      }
      // 创建单个请求的请求拦截器
      if (config.interceptors?.requestSuccessInterceptor) {
        // 直接调用,然后将处理后的config返回
        config = config.interceptors.requestSuccessInterceptor(config)
      }
      this.instance
        .request<any, T>(config)
        .then((res) => {
          // 单个请求结束后,让其loading等于默认值。因为我们将showLoading挂载到Request实例上,状态一直被修改
          this.showLoading = DEFAULT_LOADING
          // 调用传入的响应拦截器
          if (config.interceptors?.responseSuccessInterceptor) {
            res = config.interceptors.responseSuccessInterceptor(res)
          }
          resolve(res)
        })
        .catch((err) => {
          // 单个请求结束后,让其loading等于默认值。因为我们将showLoading挂载到Request实例上,状态一直被修改
          this.showLoading = DEFAULT_LOADING
          reject(err)
        })
    })
  }


完整的index文件代码


import { ILoadingInstance } from 'element-plus/lib/components/loading'
    import axios, { AxiosInstance } from 'axios'
    import { IRequestConfig } from './type'
    import { ElLoading } from 'element-plus/lib/components'
    const DEFAULT_LOADING = true
    class Request {
      public instance: AxiosInstance
      public showLoading: boolean
      public loadingInstance?: ILoadingInstance
      constructor(config: IRequestConfig) {
        // 默认不加载loading
        this.showLoading = config.showLoading ?? DEFAULT_LOADING
        this.instance = axios.create(config)
        // 先创建实例请求拦截器
        this.instance.interceptors.request.use(
          config.interceptors?.requestSuccessInterceptor,
          config.interceptors?.requestErrorInterceptor
        )
        // 先创建实例请求拦截器
        this.instance.interceptors.response.use(
          config.interceptors?.responseSuccessInterceptor,
          config.interceptors?.responseErrorInterceptor
        )
        // 创建全局请求拦截器
        this.instance.interceptors.request.use(
          (config) => {
            console.log('全局请求成功创建的拦截器')
            if (this.showLoading) {
              // 添加加载loading
              this.loadingInstance = ElLoading.service({
                text: '正在加载,请稍等...',
                background: 'rgba(0, 0, 0, .1)',
                lock: true
              })
            }
            return config
          },
          (err) => {
            console.log('全局请求失败创建的拦截器')
            this.loadingInstance?.close()
            return err
          }
        )
        // 创建全局响应拦截器
        this.instance.interceptors.response.use(
          (config) => {
            console.log('全局响应成功创建的拦截器')
            setTimeout(() => {
              this.loadingInstance?.close()
            }, 3000)
            return config
          },
          (err) => {
            console.log('全局响应失败创建的拦截器')
            this.loadingInstance?.close()
            return err
          }
        )
      }
      // 传入的泛型是约束返回值
      request<T>(config: IRequestConfig<T>): Promise<T> {
        return new Promise((resolve, reject) => {
          // 定制该请求是否加loading。当为传入该参数时,默认为true
          if (config.showLoading === false) {
            this.showLoading = false
          }
          // 创建单个请求的请求拦截器
          if (config.interceptors?.requestSuccessInterceptor) {
            // 直接调用,然后将处理后的config返回
            config = config.interceptors.requestSuccessInterceptor(config)
          }
          this.instance
            .request<any, T>(config)
            .then((res) => {
              this.showLoading = DEFAULT_LOADING
              // 调用传入的响应拦截器
              if (config.interceptors?.responseSuccessInterceptor) {
                res = config.interceptors.responseSuccessInterceptor(res)
              }
              resolve(res)
            })
            .catch((err) => {
              this.showLoading = DEFAULT_LOADING
              reject(err)
            })
        })
      }
      get<T>(config: IRequestConfig<T>): Promise<T> {
        return this.request<T>({ ...config, method: 'GET' })
      }
      post<T>(config: IRequestConfig<T>): Promise<T> {
        return this.request<T>({ ...config, method: 'POST' })
      }
      delete<T>(config: IRequestConfig<T>): Promise<T> {
        return this.request<T>({ ...config, method: 'DELETE' })
      }
      patch<T>(config: IRequestConfig<T>): Promise<T> {
        return this.request<T>({ ...config, method: 'PATCH' })
      }
    }
    export default Request


测试


import Request from './http/request'
    import { BASE_URL, TIME_OUT } from './http/request/config'
    const request = new Request({
      baseURL: BASE_URL,
      timeout: TIME_OUT,
      showLoading: true,
      interceptors: {
        requestSuccessInterceptor(config) {
          console.log('Request实例请求成功的拦截器')
          return config
        },
        requestErrorInterceptor(err) {
          console.log('Request实例请求失败的拦截器')
          return err
        },
        responseSuccessInterceptor(res) {
          console.log('Request实例响应成功的拦截器')
          return res
        },
        responseErrorInterceptor(err) {
          console.log('Request实例响应失败的拦截器')
          return err
        }
      }
    })
    interface IRequestData {
      data: any
    }
    request
      .get<IRequestData>({
        url: 'search?keywords=海阔天空',
        showLoading: true,
        interceptors: {
          requestSuccessInterceptor(config) {
            console.log('get请求的拦截器')
            return config
          }
        }
      })
      .then((res) => {
        console.log('res ====', res)
      })


网络异常,图片无法展示
|


相关文章
|
22天前
|
JavaScript
vue封装axios(用interceptors封装)
vue封装axios(用interceptors封装)
17 0
|
22天前
|
前端开发
Axios request 封装技巧:提升代码复用和效率的步骤
在开发中,为了提高效率,通常对 Axios 进行封装,简化了请求的发送和对响应的处理。同时,统一错误处理机制有助于维护代码的清晰和一致性。本文介绍了一些高效封装 Axios 请求的方法。
Axios request 封装技巧:提升代码复用和效率的步骤
|
22天前
|
缓存 前端开发 JavaScript
前端vue3分享——项目封装axios、vite使用env环境变量
前端vue3分享——项目封装axios、vite使用env环境变量
60 0
|
22天前
|
存储 算法 JavaScript
< 今日小技巧:Axios封装,接口请求增加防抖功能 >
今天这篇文章,主要是讲述对axios封装的请求,由于部分请求可能存在延时的情况。使得接口可能存在会被持续点击(即:接口未响应的时间内,被持续请求),导致重复请求的问题,容易降低前后端服务的性能!故提出给axios封装的配置里面,新增一个防抖函数,用来限制全局请求的防抖。
< 今日小技巧:Axios封装,接口请求增加防抖功能 >
|
22天前
|
JSON JavaScript 前端开发
< 每日份知识快餐:axios是什么?如何在Vue中 封装 axios ? >
本文介绍了前端开发中常用的HTTP客户端库Axios,它基于Promise,支持浏览器和Node.js,特点是功能强大、支持Promise API和并发请求,并能拦截请求和响应。文章强调了理解Axios的内部原理和优化使用的重要性,不仅讲解了基本的安装、导入和使用方法,还阐述了为何选择Axios,包括其丰富的配置选项和良好的浏览器支持。此外,文章探讨了封装Axios的必要性,以减少重复代码和提高代码维护性,并给出了设置接口请求前缀、请求头、超时时间以及封装请求方法和拦截器的示例。通过封装,开发者可以更高效地管理和使用Axios,适应不同项目需求。
|
22天前
axios封装和配置
axios封装和配置
23 0
|
22天前
04_装饰器封装axios_get请求
04_装饰器封装axios_get请求
33 0
|
22天前
|
存储 JavaScript API
LocalStorage/sessionStorage 封装 - 基于TypeScript
文章主要介绍了如何封装一个自定义 Hooks 形式的存储服务,用于在 localStorage 或者 sessionStorage 中进行持久化存储值。通过封装的方法包括 get、set、remove、clearExcept 和 clearAll,使得操作存储变得更加简单和方便。
40 1
|
22天前
|
前端开发
AJAX发送请求方法封装和请求函数底层刨析以及axios二次封装
AJAX发送请求方法封装和请求函数底层刨析以及axios二次封装
|
22天前
|
资源调度 JavaScript API