vue3封装axios

119 阅读2分钟

1. 目录层级

  • axios
    • request
      • config.ts
      • index.ts
      • types.ts
    • index.ts

2. 具体代码

2.1 request/types

import type { AxiosRequestConfig, AxiosResponse } from 'axios'

// 自定义拦截器type
export interface CustomInterceptors<T = AxiosResponse> {
  requestInterceptor?: (config: AxiosRequestConfig) => AxiosRequestConfig
  requestInterceptorCatch?: (err: any) => any
  responsetInterceptor?: (config: T) => T
  responsetInterceptorCatch?: (err: any) => any
}

// 自定义请求设置type
export interface CustomRequestConfig<T = AxiosResponse> extends AxiosRequestConfig {
  interceptors?: CustomInterceptors<T>
  showLoading?: boolean
}

2.2 request/config

// 配置环境变量

// timeout
export const TIME_OUT = 1000

// baseUrl
export const API_BASE_URL = process.env.VUE_APP_BASE_URL

2.3 request/index

import axios from 'axios'
import type { AxiosInstance } from 'axios'
import { ElLoading } from 'element-plus'
import type { LoadingInstance } from 'element-plus/lib/components/loading/src/loading'
import NProgress from 'nprogress'
import 'nprogress/nprogress.css'

import { CustomInterceptors, CustomRequestConfig } from './types'

// 默认loading为false
const DEFAULT_LOADING = false

// http请求
class HttpRequest {
  // axios实例
  instance: AxiosInstance
  // 自定义拦截器
  interceptors?: CustomInterceptors
  showLoading: boolean
  loading?: LoadingInstance

  constructor(config: CustomRequestConfig) {
    this.instance = axios.create(config)
    this.showLoading = config.showLoading ?? DEFAULT_LOADING
    this.interceptors = config.interceptors

    // 实例拦截器
    this.instance.interceptors.request.use(
      this.interceptors?.requestInterceptor,
      this.interceptors?.requestInterceptorCatch
    )
    this.instance.interceptors.response.use(
      this.interceptors?.responsetInterceptor,
      this.interceptors?.responsetInterceptorCatch
    )

    // 全局拦截器
    // request
    this.instance.interceptors.request.use(
      (config) => {
        // 加载进度条
        NProgress.start()
        // 展示loading
        if (this.showLoading) {
          this.loading = ElLoading.service({
            lock: true,
            text: '正在加载...',
            background: 'rgba(0, 0, 0, 0.5)'
          })
        }
        return config
      },
      (error) => {
        return error
      }
    )
    // response
    this.instance.interceptors.response.use(
      (res) => {
        // 移除loading
        this.loading?.close()
        // 关闭进度条
        NProgress.done()
        return res
      },
      (error) => {
        // 移除loading
        this.loading?.close()
        // 关闭进度条
        NProgress.done()
        return error
      }
    )
  }

  /** 封装请求方法 */
  // request
  request<T = any>(config: CustomRequestConfig<T>): Promise<T> {
    return new Promise((resolve, reject) => {
      // request 对config处理
      if (config.interceptors?.requestInterceptor) {
        config = config.interceptors.requestInterceptor(config)
      }
      // 判断是否showLoading
      if (config.showLoading === true) {
        this.showLoading = config.showLoading
      }

      this.instance
        .request<any, T>(config)
        .then((res) => {
          // response 对数据处理
          if (config.interceptors?.responsetInterceptor) {
            res = config.interceptors.responsetInterceptor(res)
          }

          // 重置showLoading
          this.showLoading = DEFAULT_LOADING
          resolve(res)
        })
        .catch((error) => {
          // 重置showLoading
          this.showLoading = DEFAULT_LOADING
          reject(error)
        })
    })
  }

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

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

  // put
  put<T = any>(config: CustomRequestConfig<T>): Promise<T> {
    return this.request<T>({ ...config, method: 'PUT' })
  }

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

export default HttpRequest

2.4 axios/index

// 统一管理 service
import HttpRequest from './request'
import { API_BASE_URL, TIME_OUT } from './request/config'
import { tokenCatch } from '@/utils/catch'
import { isCheckTimeout } from '@/utils/auth'
import { useLoginStore } from '@/store'
import { ElMessage } from 'element-plus'
import { setTimeStamp, getAccessHeader } from '@/utils/auth'
import router from '@/router'

const httpRequest = new HttpRequest({
  baseURL: API_BASE_URL,
  timeout: TIME_OUT,

  // 实例拦截器
  interceptors: {
    requestInterceptor(config) {
      // token
      const TOKEN = tokenCatch.get()
      if (TOKEN) {
        // TODO: 超时判断
        if (isCheckTimeout()) {
          const loginStore = useLoginStore()
          loginStore.clearUserCatchAction()
          ElMessage.error('登录超时!')
          Promise.reject(new Error('登录超时!'))
        }
        config.headers!.token = TOKEN
      }
      return config
    },
    requestInterceptorCatch(error) {
      return error
    },

    responsetInterceptor(res) {
      const blackList = [401, 404, 500, 403]
      const { code, message = '' } = res.data
      if (blackList.includes(code)) {
        // 404、401、500
        ElMessage.error(message)
        if (code === 401) {
          // 未授权,退出到
          const loginStore = useLoginStore()
          loginStore.clearUserCatchAction()
        } else if (code === 403) {
          // 无权限
        } else {
          router.push('/404')
        }

        return Promise.reject(new Error(message))
      }

      if (code === 400) {
        ElMessage.warning(message)
      }
      setTimeStamp()
      // ElMessage.success(message)
      // 通过响应头获取token
      const NEW_TOKEN = res.headers?.token ?? ''
      getAccessHeader(NEW_TOKEN)
      return res.data
    },
    responsetInterceptorCatch(error) {
      if (error.code === 'ERR_NETWORK') {
        ElMessage.error('当前网络错误,请稍后再试')
      }
      if (error && error.code === 'ECONNABORTED') {
        // 请求超时
        ElMessage.error({ message: error.message, customClass: 'custom-message' })
      }
      return error
    }
  }
})

export default httpRequest