Based on the idea of coderWhy teaching video, a simple summary of Axios, thanks again coderWhy god

Axios official website: Axios (axios-http.com)

Basic installation

// NPM install axios // YARN add axiosCopy the code

The basic use

axios(config)
axios.request(config)
axios.get(config)
axios.post(config)
Copy the code

The disadvantage is that the coupling degree is too high, the same configuration needs to be repeated many times, and not flexible

Packaging ideas

  • Using AXIos directly is too dependent and will be troublesome if you change the network request library in the future
  • Some common request functions require a configuration rewrite for each request
  • willaixosAdd another layer to wrap AXIos as customrequestIn the future, you can directly use Request to send network requests. If you want to change the network request library in the future, you can directly modify itrequestLayer to encapsulate some common functions in the Request layer, such as network request header additionAuthorization(i.e.tokenLoad),loadingEffects, etc. Interceptors can be wrapped flexibly

Some pre-knowledge grooming for Typescript encapsulation

  1. Axios (config) in the type of the config for AxiosRequestConfig, specific see website: request configuration | axios Chinese document (axios – HTTP. Cn)

    The commonly used several: url, method, baseUrl, data, a timeout, and so on

    The default configuration can be upgraded to create a new interface to implement AxiosRequestConfig and add some new configurations

  2. AxiosResponse- The default return value type of axios. The receive generic T defaults to any, representing the actual returned data type received. T is typically set to IDataType.

    // Normally we only use data
    export interface AxiosResponse<T = any>  {
      data: T;
      status: number;
      statusText: string;
      headers: any; config: AxiosRequestConfig; request? :any;
    }
    // In general, we define the return data type as follows
    export interface IDataType<T = any> {
      status: string
      msg: string
      data: T
    }
    Copy the code

    In everyday use, we do not use AxiosResponse as the encapsulated return value data type. Instead, we extract the data in it, which can be achieved by returning result.data in the response success interceptor (more on this later), and data is of type IDataType

  3. Encapsulation is done using the request method of the native instance

    //T defaults to any and returns AxiosResponse
            
    request<T = any, R = AxiosResponse<T>> (config: AxiosRequestConfig): Promise<R>;
    Copy the code

    After encapsulation, the effect is as follows:

    //T is of type IDataType, which returns a Promise
            
    xxRequest.get<T>(config: xxRequestConfig<T>): Promise<T>
    Copy the code

1. Encapsulation 1 (decoupling)

  1. Create a custom network request class, JJRequest, wrapped around an AXIos instance

    //service/request/request.ts
    class JJRequest {
      instance: AxiosInstance
    
      constructor(config: AxiosRequestConfig) {
        this.instance = axios.create(config)
      }
    
      // Return a Promise with the result type AxiosResponse
            
      request(config: AxiosRequestConfig): Promise<AxiosResponse> {
        return new Promise<AxiosResponse>((resolve, reject) = > {
          this.instance
            .request(config)
            .then((res) = > {
              resolve(res)
            })
            .catch((err) = > {
              reject(err)
            })
        })
      }
    }
    Copy the code
  2. Creating an instance (just basic baseURL and Timeout, instance-level configuration)

    //service/index.ts
    // Use the environment configuration
    const jjRequest = new JJRequest({
      baseURL: process.env.VUE_APP_BASE_URL,
      timeout: process.env.VUE_APP_TIME_OUT
    })
    Copy the code
  3. The specific use

    //main.ts
    jjRequest
      .request({
        url: '/xxx'
      })
      .then((res) = > {
        console.log(res)
      })
    Copy the code

Viewing the result: You can see that the result isAxiosResponse<any>type

Encapsulation 2 (add global level interception)

Native of interceptors in axiosInstance instances, using method is (source website: interceptors | Axios Chinese document (Axios – HTTP. Cn))

Note: The status code 2xx is used as the limit to determine whether the response is successful or failed

// Add request interceptor
axios.interceptors.request.use(function (config) {
    // What to do before sending the request
    return config;
  }, function (error) {
    // What to do about the request error
    return Promise.reject(error);
  });

// Add a response interceptor
axios.interceptors.response.use(function (response) {
    // all status codes in the 2xx range trigger this function.
    // What to do with the response data
    return response;
  }, function (error) {
    // Any status code outside the 2xx range triggers this function.
    // Do something about the response error
    return Promise.reject(error);
  });
Copy the code

Global interception is implemented in the JJRequest constructor

//service/request/request.ts
// Omit the first and second parts of the code
//....
constructor(config: AxiosRequestConfig) {
  this.instance = axios.create(config)
  
  // Global request interception
  this.instance.interceptors.request.use(
      (config) = > {
        console.log(config)
        return config
      },
      (error) = > {
        console.log('Global request failure interception', error)
      }
    )
  // Global response interception
    this.instance.interceptors.response.use(
      (res) = > {
        // Res is of AxiosResponse type with config\data\headers\ Request \status\statusText
        console.log(res)
        // Modify the data type returned, that is, AxiosResponse's data is returned
        return res.data
      },
      (error) = > {
        console.log('Global Response failure interception')
        console.log(error.request)//
        console.log(error.response)
        return error
      }
    )
  
  // add generic qualification, return data type T,
  request<T>(config: AxiosRequestConfig<T>): Promise<T> {
    return new Promise<T>((resolve, reject) = > {
      this.instance
        .request<any, T>(config)
        .then((res) = > {
          resolve(res)
        })
        .catch((err) = > {
          reject(err)
        })
    })
  }
}
Copy the code

The actual usage is as follows

//main.ts==> The data type returned is qualified to IDataType
jjRequest
  .request<IDataType>({
    url: '/xxx'
  })
  .then((res) = > {
    console.log(res)
  })
  .catch((err) = > {
    console.log('= = = = =', err)
  })
Copy the code

When the same interface is run in main.ts, the effect is as follows. Only data in AxiosResponse is left, which is the user-defined IDataType type

Demonstration interception of response failure :(change/XXX in the back-end interface to/XXXX, then the front-end access to/XXX will fail 404)

3. Encapsulation 3 (custom instance level interceptor, add token)

The effects to be achieved are as follows:

//service/index.ts
const jjRequest = new JJRequest({
  baseURL: process.env.VUE_APP_BASE_URL,
  timeout: process.env.VUE_APP_TIME_OUT,
  // An instance-level interceptor that is carried when an AXIOS instance is created
  interceptors: {requestInterceptor:...requestInterceptorCatch:...responseInterceptor:...responseInterceptorCatch:... }})Copy the code

Since there is no interceptor configuration for this property in the native AxiosRequestConfig, you need to customize the interceptor interface and request configuration interface

  1. Custom interceptor interface (four interceptors, four functions)

    interface IJJRequestInterceptors<T = AxiosResponse> {
      // The request succeededrequestInterceptor? :(config: AxiosRequestConfig) = > AxiosRequestConfig
      // The request failedrequestInterceptorCatch? :(error: any) = > any
      // The response succeededresponseInterceptor? :(res: T) = > T
      // The response failedresponseInterceptorCatch? :(error: any) = > any
    }
    Copy the code
  2. Custom request configuration interface, optional properties, configurable and unconfigurable

    interface IJJRequestConfig<T = AxiosResponse> extendsAxiosRequestConfig { interceptors? : IJJRequestInterceptors<T> }Copy the code
  3. Modify the network request class

    class JJRequest {
      instance: AxiosInstance
      // This attribute is obtained from the instanceinterceptors? : IJJRequestInterceptorconstructor(config: IJJRequestConfig) {
        this.instance = axios.create(config)
        // Get the interceptor configuration from the instance config
        this.interceptors = config.interceptors
    
        // Global request interception
        this.instance.interceptors.request.use(... ,...).// Global response interception
        this.instance.interceptors.response.use(... ,...).// instance level interception
    		this.instance.interceptors.request.use(
          this.interceptors?.requestInterceptor,
          this.interceptors?.requestInterceptorCatch
        )
        this.instance.interceptors.response.use(
          this.interceptors?.responseInterceptor,
          this.interceptors?.responseInterceptorCatch
        )
      }
    }
    Copy the code
  4. Create an instance

    const jjRequest = new JJRequest({
      baseURL: process.env.VUE_APP_BASE_URL,
      timeout: process.env.VUE_APP_TIME_OUT,
      interceptors: {
        requestInterceptor: (config) = > {
          // The token can be obtained from localStorage during development. The token is usually obtained from the server and stored in vuEX, and then stored in localStorage. The method about localStorage is encapsulated by itself
          //const token = localCache.getCache('token')
          const token = 'this ia a token'
          if (token) {
            config.headers.Authorization = `Bearer ${token}`
          }
          return config
        }
      }
    })
    Copy the code

The results show

4. Encapsulation 4 (single request invocation level interception)

To implement call-level interception, the request method needs to be upgraded from AxiosRequestConfig to IJJRequestConfig. The idea is to call this method directly

//service/request/request.ts
/ /...
request<T>(config: IJJRequestConfig<T>): Promise<T> {
    return new Promise<T>((resolve, reject) = > {
      // Request interception set location
      if (config.interceptors?.requestInterceptor) {
        config = config.interceptors.requestInterceptor(config)
      }

      this.instance
        .request<any, T>(config)
        .then((res) = > {
          // Response interception set position
          if (config.interceptors?.responseInterceptor) {
            res = config.interceptors.responseInterceptor(res)
          }
          resolve(res)
        })
        .catch((err) = > {
          console.log('= = = = =', err)
          reject(err)
        })
    })
  }
Copy the code

Called as follows

jjRequest
  .request<IDataType>({
    url: '/xxx'.// The individual request invocation level sets the interceptor separately
    interceptors: {
      // Directly returns the data attribute entry in the result of type IDataType
      responseInterceptor: (res) = > res.data
    }
  })
  .then((res) = > {
    console.log(res)
  })
  .catch((err) = > {
    console.log('= = = = =', err)
  })
Copy the code

The results are as follows

Encapsulate get, POST, etc., easy to call

//service/request/request.ts //.... get<T = any>(config: IJJRequestConfig<T>): Promise<T> { return this.request<T>({ ... config, method: 'GET' }) } post<T = any>(config: IJJDRequestConfig<T>): Promise<T> { return this.request<T>({ ... Config, method: 'POST'})} // Patch and delete can be invoked directly using jjRequest.get<IDataType>(config) to request dataCopy the code

The encapsulation of AXIos is divided into three layers

  • Global layer
  • Instance layer
  • Single request layer

In the future, we can do the corresponding encapsulation according to the actual situation. The main consideration is to put the encapsulation on which layer to do. This paper is mainly to write the idea of encapsulation

For example, loading can be done globally, in the instance layer, or in a single request, depending on specific requirements! Here is not to do specific packaging, only to do the role of a brick to attract jade!

Added: Order of execution of interception

There are many more axios wrappers, such as wrappers for repeated requests, parameter serialization, and so on, on demand. Ps: mainly I can’t, ha ha.

From a amateur program amateur little brother’s summary, inadequacies, please big guy pointed out!