通过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)
      })


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


相关文章
|
1月前
|
资源调度 JavaScript 前端开发
vue-element-admin 综合开发四:axios安装和封装、mock安装/学习/使用
这篇文章是关于如何在Vue项目中使用axios进行网络请求、二次封装axios以及使用mockjs模拟响应数据的教程。
80 1
vue-element-admin 综合开发四:axios安装和封装、mock安装/学习/使用
|
2月前
封装axios的get、post方法
本文介绍了如何封装axios的get和post方法,并展示了具体的代码实现,包括使用axios创建实例、设置请求拦截器以及定义get和post函数。
110 2
|
1月前
vue3 + Ts 中 使用 class 封装 axios
【10月更文挑战第8天】
116 1
|
1月前
vue3 + Ts 中 使用 class 封装 axios
【10月更文挑战第5天】
142 1
|
1月前
|
前端开发 JavaScript API
自己动手封装axios通用方法并上传至私有npm仓库:详细步骤与实现指南
自己动手封装axios通用方法并上传至私有npm仓库:详细步骤与实现指南
76 0
|
1月前
|
JavaScript API 开发工具
vue尚品汇商城项目-day02【11.对axios二次封装+12.接口统一管理】
vue尚品汇商城项目-day02【11.对axios二次封装+12.接口统一管理】
32 0
|
3月前
|
JSON JavaScript 前端开发
【Vue面试题二十四】、Vue项目中有封装过axios吗?主要是封装哪方面的?
这篇文章讨论了在Vue项目中封装axios的最佳实践,包括设置接口请求前缀、请求头、状态码、请求方法的封装,以及如何使用请求和响应拦截器来处理token和响应状态,从而简化HTTP请求的配置和错误处理,提高代码的可维护性。
【Vue面试题二十四】、Vue项目中有封装过axios吗?主要是封装哪方面的?
|
4月前
|
存储 开发框架 前端开发
基于SqlSugar的开发框架循序渐进介绍(10)-- 利用axios组件的封装,实现对后端API数据的访问和基类的统一封装处理
基于SqlSugar的开发框架循序渐进介绍(10)-- 利用axios组件的封装,实现对后端API数据的访问和基类的统一封装处理
|
4月前
|
前端开发
网页设计,若依项目修改(It must be done)02------axios封装后发get请求,axios请求的位置在呢?
网页设计,若依项目修改(It must be done)02------axios封装后发get请求,axios请求的位置在呢?
|
4月前
|
JavaScript API 数据处理
【Vue3+TypeScript】CRM系统项目搭建之 — Axiox 网络请求封装(二)
【Vue3+TypeScript】CRM系统项目搭建之 — Axiox 网络请求封装(二)
47 0