从零开始搭Vue项目-02 axios的封装

129 阅读7分钟

axios

axios的基本使用

  • 调用axios,传入一个url后会返回一个类型为

  • AxiosResponse的promise类型(已经执行了resolev和reject判断)

  • 用人话说就是axios将请求数据包裹在promise中返回

    axios
      .get('http://httpbin.org/get', {
      params: {
        name: 'codermjjh',
        age: '18'
      }
    })
      .then((res) => {
      console.log(res.data)
    })
    
    axios
      .post('http://httpbin.org/post', {
      data: {
        name: 'codermjjh',
        age: '18'
      }
    })
      .then((res) => {
      console.log(res.data)
    })
    

axios的全局配置

// axios的全局配置选项
// 默认url
axios.defaults.baseURL = 'http://httpbin.org'
// 超时时间
axios.defaults.timeout = 10000

axios
  .get('/get', {
    params: {
      name: 'codermjjh',
      age: '18'
    }
  })
  .then((res) => {
    console.log(res.data)
  })
axios
  .post('/post', {
    data: {
      name: 'codermjjh',
      age: '18'
    }
  })
  .then((res) => {
    console.log(res.data)
  })

axios.all

// axios.all(就是用的promise里的方法)
axios
  .all([
  axios.get('/get', { params: { name: 'codermjjh', age: 18 } }),
  axios.post('/post', { data: { name: 'codermjjh', age: 18 } })
])
  .then((res) => {
  console.log(res[0].data)
  console.log(res[1].data)
})

拦截器

// axios的拦截器
// f1在请求发送成功时候会执行的函数
// f2在请求发送失败时候会执行的函数
axios.interceptors.request.use(
  (config) => {
    // 想做的一些事情
    // 给请求添加token
    // 触发加载动画
    console.log('请求成功的拦截')
    return config
  },
  (config) => {
    console.log('请求失败的拦截')
  }
)

// f1数据响应成功时候会执行的函数
// f2数据响应失败时候会执行的函数
axios.interceptors.response.use(
  (res) => {
    console.log('响应成功的拦截')
    return res
  },
  (err) => {
    console.log('响应失败的拦截' + err)
  }
)

封装axios

基础功能封装

核心代码

// 核心处理代码
// AxiosInstance AxiosRequestConfig是官方的类型,需要从axios库中引入
import axios, { AxiosInstance, AxiosRequestConfig } from 'axios'

class MJRequest {
  // 1 定义instance类型
  private instance: AxiosInstance
  // 2 在new过程中使constructor获取到对应的公共属性
  constructor(config: AxiosRequestConfig) {
    this.instance = axios.create(config)
  }
  // 3 获取对应的路径请求参数,携带的参数,请求方式
  //   然后发送请求,获取到请求结果
  request(config: AxiosRequestConfig): void {
    this.instance.request(config).then((res) => {
      console.log(res)
    })
  }
}
export default MJRequest

公共属性配置

import MJRequest from './request'

// 主要功能是配置一些公共属性
// 比如 baceUrl 超时时间等
const mjRequest = new MJRequest({
  baseURL: process.env.VUE_APP_BASE_URL,
  timeout: process.env.VUE_APP_TIME_OUT
})

export default mjRequest

外部文件使用

import mjRequest from './service'

// 需要传入具体的路径,携带的参数,请求方式等
mjRequest.request({
  url: '/home/multidata',
  method: 'GET'
})

代码结构

image-20220709160949802.png

拦截器封装

由于开发中不仅仅需要配置公共属性,还需要监听一些loading事件(等待请求返回的转圈圈)类似事件,所以需要在外部对拦截器进行监听

拦截器有四种对应以下几种状态

requestInterceptor请求成功的拦截
requestInterceptorCatch请求失败的拦截
responseInterceptor响应成功的拦截
responseInterceptorCatch响应失败的拦截
  1. 添加自定义拦截器类型

    interface MJRequestInterceptors {
      requestInterceptor: (config: AxiosRequestConfig) => AxiosRequestConfig
      requestInterceptorCatch: (error: any) => any
      responseInterceptor: (res: AxiosResponse) => AxiosResponse
      responseInterceptorCatch: (error: any) => any
    }
    
  2. 定义自己的类型 继承axios原始类型的同时集成自定义拦截器的类型

interface MJRequestConfig extends AxiosRequestConfig {
  interceptors: MJRequestInterceptors
}
  1. 引入类型 并且替换new创建时constructor传入的类型 最后实现拦截器方法
private interceptors: MJRequestInterceptors
constructor(config: MJRequestConfig) {
  this.instance = axios.create(config)
  this.interceptors = config.interceptors

  this.instance.interceptors.request.use(
    this.interceptors.requestInterceptor,
    this.interceptors.requestInterceptorCatch
  )
  this.instance.interceptors.response.use(
    this.interceptors.responseInterceptor,
    this.interceptors.responseInterceptorCatch
  )
}
  1. 可选

由于用户也有可能不需要使用拦截器功能,所以拦截器功能应该是可选的

核心代码

import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios'

// 添加自定义拦截器类型
interface MJRequestInterceptors {
  requestInterceptor?: (config: AxiosRequestConfig) => AxiosRequestConfig
  requestInterceptorCatch?: (error: any) => any
  responseInterceptor?: (res: AxiosResponse) => AxiosResponse
  responseInterceptorCatch?: (error: any) => any
}
// 定义自己的类型 继承axios原始类型的同时集成自定义拦截器的类型
interface MJRequestConfig extends AxiosRequestConfig {
  interceptors?: MJRequestInterceptors
}

class MJRequest {
  // 引入类型
  private instance: AxiosInstance
  private interceptors?: MJRequestInterceptors
  // 创建MJREsquest时候传入的选项
  constructor(config: MJRequestConfig) {
    this.instance = axios.create(config)
    // 实现拦截器方法
    this.interceptors = config.interceptors

    this.instance.interceptors.request.use(
      this.interceptors?.requestInterceptor,
      this.interceptors?.requestInterceptorCatch
    )
    this.instance.interceptors.response.use(
      this.interceptors?.responseInterceptor,
      this.interceptors?.responseInterceptorCatch
    )
  }
  // 调用函数发出请求
  request(config: AxiosRequestConfig): void {
    this.instance.request(config).then((res) => {
      console.log(res)
    })
  }
}

export default MJRequest

公共属性+拦截器配置

import MJRequest from './request'

const mjRequest = new MJRequest({
  baseURL: process.env.VUE_APP_BASE_URL,
  timeout: process.env.VUE_APP_TIME_OUT,
  interceptors: {
    requestInterceptor: (config) => {
      console.log('请求成功的拦截')
      return config
    },
    // requestInterceptorCatch: (err) => {
    //   console.log('请求失败的拦截')
    //   return err
    // },
    responseInterceptor: (res) => {
      console.log('响应成功的拦截')
      return res
    }
    // responseInterceptorCatch: (err) => {
    //   console.log('响应失败的拦截')
    //   return err
    // }
  }
})

export default mjRequest

外部调用

import mjRequest from './service'

// 需要传入具体的路径,携带的参数,请求方式等
mjRequest.request({
  url: '/home/multidata',
  method: 'GET'
})

分层处理拦截

由于在开发种会遇到对全局请求,单实例请求,单个请求等不同的拦截场景,所以需要对多出进行拦截请求

所以添加了所有实例都会触发的拦截器和单个请求可选的拦截器

  1. 添加所有实例都会触发的拦截器

    // 添加所有实例都有的拦截器
    this.instance.interceptors.request.use(
      (config) => {
        console.log('All_Request_success')
        return config
      },
      (err) => {
        console.log('All_Request_error')
        return err
      }
    )
    
    this.instance.interceptors.response.use(
      (config) => {
        console.log('All_Response_success')
        return config
      },
      (err) => {
        console.log('All_Response_error')
        return err
      }
    )
    
  2. 添加单个请求可选的拦截器

    判断传入的参数是否有携带insterceptors属性

    request(config: MJRequestConfig): void {
      // 单个请求的拦截器
      if (config.interceptors?.requestInterceptor) {
        config = config.interceptors.requestInterceptor(config)
      }
      this.instance.request(config).then((res) => {
        if (config.interceptors?.responseInterceptor) {
          config = config.interceptors.responseInterceptor(res)
        }
        console.log(res)
      })
    }
    

核心代码

import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios'

// 添加自定义拦截器类型
interface MJRequestInterceptors {
  requestInterceptor?: (config: AxiosRequestConfig) => AxiosRequestConfig
  requestInterceptorCatch?: (error: any) => any
  responseInterceptor?: (res: AxiosResponse) => AxiosResponse
  responseInterceptorCatch?: (error: any) => any
}
// 定义自己的类型 继承axios原始类型的同时集成自定义拦截器的类型
interface MJRequestConfig extends AxiosRequestConfig {
  interceptors?: MJRequestInterceptors
}

class MJRequest {
  // 引入类型
  private instance: AxiosInstance
  private interceptors?: MJRequestInterceptors
  // 创建MJREsquest时候传入的选项
  constructor(config: MJRequestConfig) {
    this.instance = axios.create(config)
    // 实现拦截器方法
    this.interceptors = config.interceptors

    // 单个实例独有的拦截器
    this.instance.interceptors.request.use(
      this.interceptors?.requestInterceptor,
      this.interceptors?.requestInterceptorCatch
    )
    this.instance.interceptors.response.use(
      this.interceptors?.responseInterceptor,
      this.interceptors?.responseInterceptorCatch
    )

    // 添加所有实例都有的拦截器
    this.instance.interceptors.request.use(
      (config) => {
        console.log('All_Request_success')
        return config
      },
      (err) => {
        console.log('All_Request_error')
        return err
      }
    )

    this.instance.interceptors.response.use(
      (config) => {
        console.log('All_Response_success')
        return config
      },
      (err) => {
        console.log('All_Response_error')
        return err
      }
    )
  }
  // 调用请求发出请求
  request(config: MJRequestConfig): void {
    if (config.interceptors?.requestInterceptor) {
      config = config.interceptors.requestInterceptor(config)
    }
    this.instance.request(config).then((res) => {
      if (config.interceptors?.responseInterceptor) {
        config = config.interceptors.responseInterceptor(res)
      }
      console.log(res)
    })
  }
}

export default MJRequest

公共属性+拦截器配置

import MJRequest from './request'

const mjRequest = new MJRequest({
  baseURL: process.env.VUE_APP_BASE_URL,
  timeout: process.env.VUE_APP_TIME_OUT,
  interceptors: {
    requestInterceptor: (config) => {
      console.log('请求成功的拦截')
      return config
    },
    // requestInterceptorCatch: (err) => {
    //   console.log('请求失败的拦截')
    //   return err
    // },
    responseInterceptor: (res) => {
      console.log('响应成功的拦截')
      return res
    }
    // responseInterceptorCatch: (err) => {
    //   console.log('响应失败的拦截')
    //   return err
    // }
  }
})

export default mjRequest

外部调用

mjRequest.request({
  url: '/home/multidata',
  method: 'GET',
  interceptors: {
    requestInterceptor: (config) => {
      console.log('单独请求的config')
      return config
    },
    responseInterceptor: (res) => {
      console.log('单独响应的response')
      return res
    }
  }
})

携带Token

错误返回

在全局拦截器下,监听返回数据的状态码

this.instance.interceptors.response.use(
        (res) => {
          console.log('All_Response_success')
          switch (res.data.returnCode) {
            case '-1001':
              console.log('请求失败错误信息....')
              break
            // 以此类推...
            default:
              break
          }

          return res.data
        },
        (err) => {
          console.log('All_Response_error')
          switch (err.response.status) {
            case 404:
              console.log('404错误...')
              break
            // 以此类推...
            default:
              break
          }
          return err
        }
      )

组件loading加载

  1. 引入element plus

    在代码中声明elementPlus组件时候需要对其做一次引入,同时需要引入其内的css样式

    import { ElLoading } from 'element-plus'
    // 在代码中引用需要手动导入对应组件的样式
    import 'element-plus/es/components/loading/style/css'
    

    路径不是绝对的随着版本更新会改变,所以建议去翻elementPlus源码

  2. 使用loading

    在全局拦截器中声明

    this.instance.interceptors.request.use(
      (config) => {
        console.log('All_Request_success')
        if (this.showLoading) {
          this.loading = ElLoading.service({
            lock: true,
            text: '努力搬运中',
            background: 'rgba(0,0,0,0.5)'
          })
        }
        return config
      },
      (err) => {
        console.log('All_Request_error')
        return err
      }
    )
    
  3. 停止loading事件

    在请求响应完毕之后需要停止loading事件,由于typescript的原因我们需要引入loading的类型

    import { LoadingInstance } from 'element-plus/es/components/loading/src/loading'
    

    这里的路径也不是绝对的建议翻看elementPlus源码

    // 最终代码
    
    import axios, { AxiosInstance, AxiosRequestConfig } from 'axios'
    
    import { ElLoading } from 'element-plus'
    import { LoadingInstance } from 'element-plus/es/components/loading/src/loading'
    // 在代码中引用需要手动导入对应组件的样式
    import 'element-plus/es/components/loading/style/css'
    
    // 添加自定义拦截器类型
    interface MJRequestInterceptors {
      requestInterceptor?: (config: AxiosRequestConfig) => AxiosRequestConfig
      requestInterceptorCatch?: (error: any) => any
      responseInterceptor?: (res: any) => any
      responseInterceptorCatch?: (error: any) => any
    }
    // 定义自己的类型 继承axios原始类型的同时集成自定义拦截器的类型
    interface MJRequestConfig extends AxiosRequestConfig {
      interceptors?: MJRequestInterceptors
      showLoading?: boolean
    }
    const DEFAULT_LOADING = false
    
    class MJRequest {
      // 引入类型
      private instance: AxiosInstance
      private interceptors?: MJRequestInterceptors
      private loading?: LoadingInstance
      private showLoading: boolean
      // 创建MJREsquest时候传入的选项
      constructor(config: MJRequestConfig) {
        // 获取传入数据
        this.instance = axios.create(config)
        this.interceptors = config.interceptors
        this.showLoading = config.showLoading ?? DEFAULT_LOADING
        // 单个实例独有的拦截器
        this.instance.interceptors.request.use(
          this.interceptors?.requestInterceptor,
          this.interceptors?.requestInterceptorCatch
        )
        this.instance.interceptors.response.use(
          this.interceptors?.responseInterceptor,
          this.interceptors?.responseInterceptorCatch
        )
    
        // 添加所有实例都有的拦截器
        this.instance.interceptors.request.use(
          (config) => {
            console.log('All_Request_success')
            if (this.showLoading) {
              this.loading = ElLoading.service({
                lock: true,
                text: '努力搬运中',
                background: 'rgba(0,0,0,0.5)'
              })
            }
            return config
          },
          (err) => {
            console.log('All_Request_error')
            return err
          }
        )
    
        this.instance.interceptors.response.use(
          (res) => {
            console.log('All_Response_success')
            setTimeout(() => {
              this.loading?.close()
            }, 1000)
            switch (res.data.returnCode) {
              case '-1001':
                console.log('请求失败错误信息....')
                break
              // 以此类推...
              default:
                break
            }
    
            return res.data
          },
          (err) => {
            console.log('All_Response_error')
            this.loading?.close()
            switch (err.response.status) {
              case 404:
                console.log('404错误...')
                break
              // 以此类推...
              default:
                break
            }
            return err
          }
        )
      }
      // 调用请求发出请求
      request<T>(config: MJRequestConfig): Promise<T> {
        return new Promise((resolve, reject) => {
          // 获取数据
          if (config.interceptors?.requestInterceptor) {
            config = config.interceptors.requestInterceptor(config)
          }
          if (config.showLoading === true) {
            this.showLoading = config.showLoading
          }
          this.instance
            .request<any, T>(config)
            .then((res) => {
              // 单个请求的拦截器
              if (config.interceptors?.responseInterceptor) {
                res = config.interceptors.responseInterceptor(res)
              }
              // 重置loading
              this.showLoading = DEFAULT_LOADING
              // 返回res
              resolve(res)
            })
            .catch((err) => {
              reject(err)
              // 重置loading
              this.showLoading = DEFAULT_LOADING
            })
        })
      }
    }
    
    export default MJRequest
    

获取Token请求

因为tonke是存放在Vuex中的所以我们应该去vuex中获取,但是现在还没有获取到Token所以先定义静态资源代替

公共属性配置

const token = ''
if (token) {
  config.headers.Aunthorization = `Bearer ${token}`
}

封装请求方式

get<T = any>(config: AxiosRequestConfig): Promise<T> {
  return this.request({ ...config, method: 'GET' })
}

post<T = any>(config: AxiosRequestConfig): Promise<T> {
  return this.request({ ...config, method: 'POST' })
}

patch<T = any>(config: AxiosRequestConfig): Promise<T> {
  return this.request({ ...config, method: 'PATCH' })
}

delete<T = any>(config: AxiosRequestConfig): Promise<T> {
  return this.request({ ...config, method: 'DELETE' })
}

最终代码

核心代码

import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios'

import { ElLoading } from 'element-plus'
import { LoadingInstance } from 'element-plus/es/components/loading/src/loading'
// 在代码中引用需要手动导入对应组件的样式
import 'element-plus/es/components/loading/style/css'

// 添加自定义拦截器类型
interface MJRequestInterceptors<T = AxiosResponse> {
  requestInterceptor?: (config: AxiosRequestConfig) => AxiosRequestConfig
  requestInterceptorCatch?: (error: any) => any
  responseInterceptor?: (res: T) => T
  responseInterceptorCatch?: (error: any) => any
}
// 定义自己的类型 继承axios原始类型的同时集成自定义拦截器的类型
interface MJRequestConfig<T = AxiosResponse> extends AxiosRequestConfig {
  interceptors?: MJRequestInterceptors<T>
  showLoading?: boolean
}
const DEFAULT_LOADING = true

class MJRequest {
  // 引入类型
  private instance: AxiosInstance
  private interceptors?: MJRequestInterceptors
  private loading?: LoadingInstance
  private showLoading: boolean
  // 创建MJREsquest时候传入的选项
  constructor(config: MJRequestConfig) {
    // 获取传入数据
    this.instance = axios.create(config)
    this.interceptors = config.interceptors
    this.showLoading = config.showLoading ?? DEFAULT_LOADING
    // 单个实例独有的拦截器
    this.instance.interceptors.request.use(
      this.interceptors?.requestInterceptor,
      this.interceptors?.requestInterceptorCatch
    )
    this.instance.interceptors.response.use(
      this.interceptors?.responseInterceptor,
      this.interceptors?.responseInterceptorCatch
    )

    // 添加所有实例都有的拦截器
    this.instance.interceptors.request.use(
      (config) => {
        // console.log('All_Request_success')
        if (this.showLoading) {
          this.loading = ElLoading.service({
            lock: true,
            text: '努力搬运中',
            background: 'rgba(0,0,0,0.5)'
          })
        }
        return config
      },
      (err) => {
        // console.log('All_Request_error')
        return err
      }
    )

    this.instance.interceptors.response.use(
      (res) => {
        // console.log('All_Response_success')
        setTimeout(() => {
          this.loading?.close()
        }, 1000)
        switch (res.data.returnCode) {
          case '-1001':
            console.log('请求失败错误信息....')
            break
          // 以此类推...
          default:
            break
        }

        return res.data
      },
      (err) => {
        // console.log('All_Response_error')
        this.loading?.close()
        switch (err.response.status) {
          case 404:
            console.log('404错误...')
            break
          // 以此类推...
          default:
            break
        }
        return err
      }
    )
  }
  // 调用请求发出请求
  request<T>(config: MJRequestConfig<T>): Promise<T> {
    return new Promise((resolve, reject) => {
      // 获取数据
      if (config.interceptors?.requestInterceptor) {
        config = config.interceptors.requestInterceptor(config)
      }
      if (config.showLoading === false) {
        this.showLoading = config.showLoading
      }
      this.instance
        .request<any, T>(config)
        .then((res) => {
          // 单个请求的拦截器
          if (config.interceptors?.responseInterceptor) {
            res = config.interceptors.responseInterceptor(res)
          }
          // 重置loading
          this.showLoading = DEFAULT_LOADING
          // 返回res
          resolve(res)
        })
        .catch((err) => {
          reject(err)
          // 重置loading
          this.showLoading = DEFAULT_LOADING
        })
    })
  }
  get<T>(config: MJRequestConfig<T>): Promise<T> {
    return this.request<T>({ ...config, method: 'GET' })
  }

  post<T>(config: MJRequestConfig<T>): Promise<T> {
    return this.request<T>({ ...config, method: 'POST' })
  }

  delete<T>(config: MJRequestConfig<T>): Promise<T> {
    return this.request<T>({ ...config, method: 'DELETE' })
  }

  patch<T>(config: MJRequestConfig<T>): Promise<T> {
    return this.request<T>({ ...config, method: 'PATCH' })
  }
}

export default MJRequest

公共属性配置

import MJRequest from './request'

const timeout = parseInt(process.env.VUE_APP_TIME_OUT || '1000')

const mjRequest = new MJRequest({
  baseURL: process.env.VUE_APP_BASE_URL,
  timeout: timeout,
  interceptors: {
    requestInterceptor: (config) => {
      console.log('请求成功的拦截')
      // const token = ''
      // if (token) {
      //   config.headers.Aunthorization = `Bearer ${token}`
      // }
      return config
    },
    // requestInterceptorCatch: (err) => {
    //   console.log('请求失败的拦截')
    //   return err
    // },
    responseInterceptor: (res) => {
      console.log('响应成功的拦截')
      return res
    }
    // responseInterceptorCatch: (err) => {
    //   console.log('响应失败的拦截')
    //   return err
    // }
  }
})

export default mjRequest

外部文件使用

// mjRequest.request({
//   url: '/home/multidata',
//   method: 'GET',
//   interceptors: {
//     requestInterceptor: (config) => {
//       console.log('单独请求的config')
//       return config
//     },
//     responseInterceptor: (res) => {
//       console.log('单独响应的response')
//       return res
//     }
//   }
// })

// mjRequest.request({
//   url: '/home/multidata',
//   method: 'GET',
//   showLoading: true
// })
interface DataType {
  data: any
  returnCode: string
  success: boolean
}
mjRequest
  .get<DataType>({
    url: '/home/multidata'
  })
  .then((res) => {
    console.log(res.data)
    console.log(res.returnCode)
    console.log(res.success)
  })

代码结构

image-20220709160949802.png