Partition code file structure

  • config: Exports some global variables
  • type: Defines a type interface
  • index: the wrapper file of Axios

The config file

    // Set the request timeout.
    // Change the baseUrl according to the environment
    
    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 }
Copy the code

Type the file

If we don’t wrap Axios, we can just use the types provided by Axios. But we need to provide an interceptor function and also show loading, so we need to extend some built-in interfaces.

    import type { AxiosRequestConfig, AxiosResponse } from 'axios'
    
    // Define the incoming interceptor interface, and both are optional.
    interface IRequestInterceptors<T = AxiosResponse> {
      // Interceptor when the request succeedsrequestSuccessInterceptor? :(config: AxiosRequestConfig) = > AxiosRequestConfig
      // Interceptor when the request failsrequestErrorInterceptor? :(err: any) = > any
      // Interceptor for successful responseresponseSuccessInterceptor? :(res: T) = > T
      // Interceptor when the response failsresponseErrorInterceptor? :(err: any) = > any
    }

    // This interface will replace AxiosRequestConfig
    export interface IRequestConfig<T = AxiosResponse> extends AxiosRequestConfig {
      // Each request instance can pass without interceptorsinterceptors? : IRequestInterceptors<T>// Whether to display loadingshowLoading? :boolean
    }
Copy the code

The index file

We wrap AXIOS with classes that allow it to create multiple INSTANCES of AXIOS. It has its own unique state.

Defines the global interceptor for AXIos

His goal is that each AXIos instance and request needs to do something that we can put in the global interceptor. We define the logic of the interceptor in the constructor.

    import axios, { AxiosInstance } from 'axios'
    import { IRequestConfig } from './type'
    class Request {
      public instance: AxiosInstance

      constructor(config: IRequestConfig) {
        this.instance = axios.create(config)
        // Create a global request interceptor
        this.instance.interceptors.request.use(
          (config) = > {
            console.log('Global request successfully created interceptor')
            return config
          },
          (err) = > {
            console.log('Interceptor created by global request failure')
            return err
          }
        )
        // Create global response interceptor
        this.instance.interceptors.response.use(
          (config) = > {
            console.log('Global response successfully created interceptor')
            return config
          },
          (err) = > {
            console.log('Interceptor created for global response failure')
            return err
          }
        )
      }
    }
Copy the code

Define an instance interceptor for AXIos

These interceptors are just as global as if multiple AXIOS instances were not created. In most cases, we don’t need to create multiple instances of AXIos. This is where our extended AxiosRequestConfig interface comes in handy. These interceptors we need to pass in the corresponding logic when creating the instance. Then register in the constructor.

    import axios, { AxiosInstance } from 'axios'
    import { IRequestConfig } from './type'
    class Request {
      public instance: AxiosInstance

      constructor(config: IRequestConfig) {
        this.instance = axios.create(config)
        // Create instance request interceptor
        this.instance.interceptors.request.use(
          config.interceptors?.requestSuccessInterceptor,
          config.interceptors?.requestErrorInterceptor
        )
        // Create instance request interceptor
        this.instance.interceptors.response.use(
          config.interceptors?.responseSuccessInterceptor,
          config.interceptors?.responseErrorInterceptor
        )
      }
    }
Copy the code

encapsulationrequestfunction

This is a call to the request function in AXIos.

We know the benefits of promises, so we return request as a Promise object. But the promise needs to pass in a generic type as the type of the return value. So we need to pass in a return value type when we call the request function. And we can create an interceptor unique to a single request here. These interceptors need to pass in the corresponding logic when calling the Request function. And calls the interceptor passed in and returns its value to the configuration object or return value.

 // The generic passed in is the constraint return value
  request<T>(config: IRequestConfig<T>): Promise<T> {
    return new Promise((resolve, reject) = > {
        
      // Create a request interceptor for a single request
      if (config.interceptors?.requestSuccessInterceptor) {
        // call directly, and then return the processed config
        config = config.interceptors.requestSuccessInterceptor(config)
      }
      this.instance
        .request<any, T>(config)
        .then((res) = > {
          // Invoke the incoming response interceptor
          if (config.interceptors?.responseSuccessInterceptor) {
            res = config.interceptors.responseSuccessInterceptor(res)
          }
          resolve(res)
        })
        .catch((err) = > {
          reject(err)
        })
    })
  }
Copy the code

Encapsulate other request methods.

These methods use the request method encapsulated above.

      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'})}Copy the code

Loading is done via element-plus

Loading components can be loaded via Elder-plus. And call it as a service. Note the type import path for the new version.

    
    import { ILoadingInstance } from 'element-plus/lib/components/loading'
    import axios, { AxiosInstance } from 'axios'
    import { IRequestConfig } from './type'
    import { ElLoading } from 'element-plus/lib/components'

    // Default loading loading
    const DEFAULT_LOADING = true

    // Define two attributes
    public showLoading: boolean
    publicloadingInstance? : ILoadingInstanceconstructor(config: IRequestConfig) {
        // Loading is not loaded by default
        this.showLoading = config.showLoading ?? DEFAULT_LOADING
        this.instance = axios.create(config)
        // Create a global request interceptor
        this.instance.interceptors.request.use(
          (config) = > {
            console.log('Global request successfully created interceptor')
            // Loading loading occurs when showLoading is true
            if (this.showLoading) {
              // Add loading loading
              this.loadingInstance = ElLoading.service({
                text: 'Loading, please wait... '.background: 'rgba(0, 0, 0, .1)'.lock: true})}return config
          },
          (err) = > {
            console.log('Interceptor created by global request failure')
            // An error is requested to disable loading
            this.loadingInstance? .close()return err
          }
        )
         // Create global response interceptor
        this.instance.interceptors.response.use(
          (config) = > {
            console.log('Global response successfully created interceptor')
           // Turn loading off in response
            this.loadingInstance? .close()return config
          },
          (err) = > {
            console.log('Interceptor created for global response failure')
            // Turn loading off when the response fails
            this.loadingInstance? .close()return err
          }
        )
      }
      
  // The generic passed in is the constraint return value
  request<T>(config: IRequestConfig<T>): Promise<T> {
    return new Promise((resolve, reject) = > {
      // Specify whether to load the request. When this parameter is passed, the default is true
      if (config.showLoading === false) {
        this.showLoading = false
      }
      // Create a request interceptor for a single request
      if (config.interceptors?.requestSuccessInterceptor) {
        // call directly, and then return the processed config
        config = config.interceptors.requestSuccessInterceptor(config)
      }
      this.instance
        .request<any, T>(config)
        .then((res) = > {
          // After a single request ends, loading is set to the default value. Since we mounted showLoading to the Request instance, the state was constantly changed
          this.showLoading = DEFAULT_LOADING
          // Invoke the incoming response interceptor
          if (config.interceptors?.responseSuccessInterceptor) {
            res = config.interceptors.responseSuccessInterceptor(res)
          }
          resolve(res)
        })
        .catch((err) = > {
          // After a single request ends, loading is set to the default value. Since we mounted showLoading to the Request instance, the state was constantly changed
          this.showLoading = DEFAULT_LOADING
          reject(err)
        })
    })
  }
Copy the code

Complete index file code

    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
      publicloadingInstance? : ILoadingInstanceconstructor(config: IRequestConfig) {
        // Loading is not loaded by default
        this.showLoading = config.showLoading ?? DEFAULT_LOADING
        this.instance = axios.create(config)
        // Create an instance request interceptor
        this.instance.interceptors.request.use(
          config.interceptors?.requestSuccessInterceptor,
          config.interceptors?.requestErrorInterceptor
        )
        // Create an instance request interceptor
        this.instance.interceptors.response.use(
          config.interceptors?.responseSuccessInterceptor,
          config.interceptors?.responseErrorInterceptor
        )
        // Create a global request interceptor
        this.instance.interceptors.request.use(
          (config) = > {
            console.log('Global request successfully created interceptor')
            if (this.showLoading) {
              // Add loading loading
              this.loadingInstance = ElLoading.service({
                text: 'Loading, please wait... '.background: 'rgba(0, 0, 0, .1)'.lock: true})}return config
          },
          (err) = > {
            console.log('Interceptor created by global request failure')
            this.loadingInstance? .close()return err
          }
        )
        // Create global response interceptor
        this.instance.interceptors.response.use(
          (config) = > {
            console.log('Global response successfully created interceptor')
            setTimeout(() = > {
              this.loadingInstance? .close() },3000)
            return config
          },
          (err) = > {
            console.log('Interceptor created for global response failure')
            this.loadingInstance? .close()return err
          }
        )
      }

      // The generic passed in is the constraint return value
      request<T>(config: IRequestConfig<T>): Promise<T> {
        return new Promise((resolve, reject) = > {
          // Specify whether to load the request. When this parameter is passed, the default is true
          if (config.showLoading === false) {
            this.showLoading = false
          }
          // Create a request interceptor for a single request
          if (config.interceptors?.requestSuccessInterceptor) {
            // call directly, and then return the processed config
            config = config.interceptors.requestSuccessInterceptor(config)
          }
          this.instance
            .request<any, T>(config)
            .then((res) = > {
              this.showLoading = DEFAULT_LOADING
              // Invoke the incoming response interceptor
              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
Copy the code

test

    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 instance requested interceptor successfully ')
          return config
        },
        requestErrorInterceptor(err) {
          console.log('Request instance Request failed interceptor ')
          return err
        },
        responseSuccessInterceptor(res) {
          console.log('Request instance responds successfully to interceptor ')
          return res
        },
        responseErrorInterceptor(err) {
          console.log('Interceptor failed to respond to Request instance')
          return err
        }
      }
    })

    interface IRequestData {
      data: any
    }

    request
      .get<IRequestData>({
        url: 'search? Keywords = Sea and sky '.showLoading: true.interceptors: {
          requestSuccessInterceptor(config) {
            console.log('Get request interceptor')
            return config
          }
        }
      })
      .then((res) = > {
        console.log('res ====', res)
      })
Copy the code