“This is the 12th day of my participation in the First Challenge 2022. For details: First Challenge 2022”

axios

Axios: Ajax I/O System. A third-party library that can make network requests in both the browser and Node environment

Features:

  • Sends XMLHttpRequests requests in the browser
  • Send HTTP requests in Node.js
  • Supporting Promise API
  • Intercept requests and responses
  • Transform request and response data
  • , etc.

The basic use

A get request

// The imported axios is an instance object
import axios from 'axios'

The axios method returns a promise
const res = await axios.get('https://httpbin.org/get', {
 	// Pass the query argument
  params: {
    name: 'Klaus'}})// The actual data returned by the server is placed in the data property
console.log(res.data)
Copy the code

A post request

const res = await axios.post('https://httpbin.org/post', {
  data: {
    name: 'Klaus'}})Copy the code

Request the request

// All axios requests are essentially calling Axios's request method
const res = await axios.request({
  url: 'https://httpbin.org/post'.method: 'post'.data: {
    name: 'Klaus'}})Copy the code

all

Axios.all is essentially promise.all

const res = await axios.all([
  axios.get('https://httpbin.org/get', {
    params: {
      name: 'Klaus'
    }
  }),

  axios.post('https://httpbin.org/post', {
    data: {
      name: 'Klaus'}})])Copy the code

The interceptor

// Interceptors must be registered before requests and responses

// Request interceptor - Two arguments to the use method - resolve and reject respectively
// The resolve method takes the configuration option of the request
The reject method takes an error object
axios.interceptors.request.use(
  config= > {
    console.log(config)
    return config
  },
  err= > console.error(err)
)

// Response interceptor - Two arguments to the use method - resolve and reject respectively
// The resolve method takes the response body
The reject method takes an error object
axios.interceptors.response.use(
  res= > {
    return res.data
  },
  err= > console.error(err)
)
Copy the code

configuration

// The following are global configurations
// All global configurations of AXIos can be set in the property defaults

// Base request public path
axios.defaults.baseURL = 'http://httpbin.org'

// The timeout period
axios.defaults.timeout = 10000

// A custom request header to pass
axios.defaults.headers = {}
Copy the code
// Local configuration
axios
  .get('/get', {
  params: {
    name: 'Klaus'.age: 18
  },
  
  // Local configuration can override global configuration
  timeout: 5000.headers: {}
})
  .then((res) = > {
  console.log(res.data)
})
Copy the code

encapsulation

Introducing AXIos directly into every page where a network request is made causes the following problems:

  1. The need to introduce AXIOS on every page results in too much coupling between AXIOS and logic, and if you need to change it later, you’ll need to change it on every page that uses Axios

    Is cumbersome and error-prone

  2. When the page is requested, we pass many common configurations, such as BASE_URL, TOKEN, etc.

So we need to re-wrap our network requests – usually in the SRC service or API folder

type.ts

import { AxiosRequestConfig, AxiosResponse  } from 'axios'

// Define your own interceptor type
export interfaceInterceptor { requestInterceptor? :(config: AxiosRequestConfig) = > AxiosRequestConfig | Promise<AxiosRequestConfig>, requestInterceptorCatch? :(err: any) = > any, responseInterceptor? :(res: AxiosResponse) = > AxiosResponse | Promise<AxiosResponse>, responseInterceptorCatch? :(err: any) = > any,}// Extend the AxiosRequestConfig type of AXIOS through interface inheritance
export interface Config extendsAxiosRequestConfig{ interceptor? : Interceptor }Copy the code

env.ts

// Set some related configurations as constants before importing them
// Decouple the configuration from the call. If you need to change it, just change the value of the constant
export const TIME_OUT = 10000
export const BASE_URL = 'https://httpbin.org/'
Copy the code

index.ts

import Api from './api'

import {
  BASE_URL,
  TIME_OUT
} from './env'

const api = new Api({
  baseURL: BASE_URL,
  timeout: TIME_OUT,

  // Different instances may have different interceptors
  // So we pass the interceptor as an extended property
  // interceptor: {
  // requestInterceptor: config => {
  // console.log(' request successful ')
  // return config
  / /},
  // requestInterceptorCatch: err => console.error(err),
  // responseInterceptor: res => res.data,
  // responseInterceptorCatch: err => console.error(err)
  // }
})

export default api
Copy the code

api.ts

import axios from 'axios'
import { ElLoading } from 'element-plus'
import type { AxiosInstance, AxiosRequestConfig } from 'axios'
import type { Config } from './type'

// There are a lot of exported properties and methods, so use class encapsulation
// Because the class has better encapsulation
export default class {
  instance: AxiosInstance

  constructor(config: Config) {
    // Each time an instance is created, the axios.create method is called
    // axios.create returns an instance of axios
    // This ensures that we can create multiple axios instances with different configurations
    this.instance = axios.create(config)

    // Use interceptors if instance level interceptors exist
    // This is an instance interceptor specific to each request
    // This operation can also be done using the transformRequest and transformResponse configuration items
    if (config.interceptor) {
      const { interceptor } = config
      this.instance.interceptors.request.use(interceptor.requestInterceptor, interceptor.requestInterceptorCatch)
      this.instance.interceptors.response.use(interceptor.responseInterceptor, interceptor.responseInterceptorCatch)
    }

    // Access private methods
    this.#registerGlobalInterceptor(config)
  }

  // This is the interceptor common to all instances - global interceptor
  // If there are more than one interceptor, more than one interceptor will be executed
  #registerGlobalInterceptor(option: Config) {
    this.instance.interceptors.request.use(config= > config, err= > err)
    this.instance.interceptors.response.use(res= > res.data, err= > err)
  }

  request(config: AxiosRequestConfig) {
    return this.instance.request(config)
  }
 
  get<R = IResponse>(url: string.config: AxiosRequestConfig = {}) {
    return this.instance.get<unknown, R>(url, config)
  }

  post<T = unknown, R = IResponse>(url: string.data: T, config: AxiosRequestConfig = {}) {
    return this.instance.post<unknown, R>(url, data, config)
  }
}
Copy the code

Loading was added when the request was made

import axios from 'axios'
import { ElLoading } from 'element-plus'
import type { AxiosInstance, AxiosRequestConfig } from 'axios'
import type { LoadingInstance } from 'element-plus/es/components/loading/src/loading'
import type { Config } from './type'

// El-loading is a plug-in, not a component. Elemental-plus on demand does not introduce el-loading correctly
// You need to manually introduce the el-loading style
import 'element-plus/theme-chalk/el-loading.css'

export default class {
  instance: AxiosInstance
  loading: LoadingInstance | undefined

  constructor(config: Config) {
    this.instance = axios.create(config)

    if (config.interceptor) {
      const { interceptor } = config
      this.instance.interceptors.request.use(interceptor.requestInterceptor, interceptor.requestInterceptorCatch)
      this.instance.interceptors.response.use(interceptor.responseInterceptor, interceptor.responseInterceptorCatch)
    }

    this.#registerGlobalInterceptor()
  }

  #registerGlobalInterceptor() {
    this.instance.interceptors.request.use((config: Config) = > {
      // Make a shallow copy of the argument -- make sure the function is as pure as possible
      constcustomConfig = { ... config }/ /?? -- Returns the right-hand operand if the left-hand operand is null or undefined, otherwise returns the left-hand operand
      // Check whether this.loading exists to avoid multiple loading effects when multiple requests are sent on the same page
      if (!this.loading && (customConfig? .showLoading ??true)) {
        / / start loading
        this.loading =  ElLoading.service({
          lock: true.text: 'Loading... '.background: 'rgba (0, 0, 0, 0.7)'})},const token = Cookies.get('token')

      if (token) {
         // If there is a token, the request carries the corresponding token
         // The corresponding request header is AuthorizationcustomConfig.headers = { ... config.headers,Authorization: `Bearer ${token}`}}return customConfig
    }, err= > err)

    this.instance.interceptors.response.use(res= > {
      / / close the loading
      this.loading? .close()// Axios returns a string, so deserialization is required
      return JSON.parse(res.data)
    }, err= > {
      this.loading? .close()return err
    })
  }

  request(config: AxiosRequestConfig) {
    return this.instance.request(config)
  }
}
Copy the code

The environment variable

In development, we sometimes need to set different environment variables for different environments. There are four common environments:

  • Development environment: Development
  • Production environment: Production
  • Test environment: Test
  • Pre-release environment: stage

Configuration Mode 1: Manually switch between different environments (not recommended)

// Development environment
// const BASE_URL = 'http://example.org/dev'
// const BASE_NAME = 'Klaus'

// Production environment
// const BASE_URL = 'http://example.org/prod'
// const BASE_NAME = 'Alex'

// Test the environment
// const BASE_URL = 'http://example.org/test'
// const BASE_NAME = 'Steven'
Copy the code

Configuration mode 2: The values of process.env.node_env are differentiated

// Vite sets its own environment variables by default
// Such as MODE, PROD, DEV, SSR, BASE_URL, etc
if (import.meta.env.MODE === 'development') {
 // ... do something
} else {
 // ... do else
}
Copy the code

Configuration method 3 - Write different environment variable configuration files

# .env.production
#Note: All environment variables in Vite must begin with VITE_
VITE_APP_TITLE=My App in production
Copy the code
# .env.development
VITE_APP_TITLE=My App in development
Copy the code

When vite is packaged, it will automatically inject different environment variables depending on the development environment. We can read these environment variables and use them where we need them, such as vite configuration files, SRC source code, etc

/ / use
console.log(import.meta.env.VITE_APP_TITLE)
Copy the code