TS axios封装

方式一

service/request/request.ts

import axios from 'axios'
import { ElLoading } from 'element-plus'
import type { AxiosRequestConfig, AxiosInstance, AxiosResponse } from 'axios'
import type { ILoadingInstance } from 'element-plus/lib/el-loading/src/loading.type'

// import type { LoadingInstance } from "element-plus/lib/components/loading/src/loading"; // 按需引入

/**
 * 封装axios
 * 这里使用类进行封装是因为类具有更强的一个封装性
 * 比单纯的用函数去进行封装要更好一些
 * 使用方式:LWJRequest.get()
 */
// 拦截器类型约束--接口
// 可以让不同的类拥有不同的拦截器,更加灵活
interface InterceptorHooks {
  requestInterceptor?: (config: AxiosRequestConfig) => AxiosRequestConfig
  requestInterceptorCatch?: (error: any) => any

  responseInterceptor?: (response: AxiosResponse) => AxiosResponse
  responseInterceptorCatch?: (error: any) => any
}

// 类接口
interface LWJRequestConfig extends AxiosRequestConfig {
  showLoading?: boolean
  interceptorHooks?: InterceptorHooks
}

// 属性接口
interface LWJData<T> {
  data: T
  returnCode: string
  success: boolean
}

// 封装请求类
class LWJRequest {
  config: AxiosRequestConfig
  interceptorHooks?: InterceptorHooks
  showLoading: boolean
  loading?: ILoadingInstance
  instance: AxiosInstance

  constructor(options: LWJRequestConfig) {
    this.config = options
    this.interceptorHooks = options.interceptorHooks
    this.showLoading = options.showLoading ?? true
    this.instance = axios.create(options)

    this.setupInterceptor()
  }

  // 拦截器函数
  setupInterceptor(): void {
    // 请求拦截
    this.instance.interceptors.request.use(
      this.interceptorHooks?.requestInterceptor,
      this.interceptorHooks?.requestInterceptorCatch
    )

    // 响应拦截
    this.instance.interceptors.response.use(
      this.interceptorHooks?.responseInterceptor,
      this.interceptorHooks?.responseInterceptorCatch
    )

    // 添加所有实例都有的拦截器--请求拦截器
    this.instance.interceptors.request.use((config) => {
      if (this.showLoading) {
        this.loading = ElLoading.service({
          lock: true,
          text: 'Loading',
          spinner: 'el-icon-loading',
          background: 'rgba(0, 0, 0, 0.7)'
        })
      }
      return config
    })

    // 正在加载效果--响应拦截器
    this.instance.interceptors.response.use(
      (res) => {
        // setTimeout(()=>{
        //   this.loading?.close()
        // },1000)
        this.loading?.close()
        return res
      },
      (err) => {
        this.loading?.close()
        // if(err.response.status === 404){
        // }
        return err
      }
    )
  }

  // 某一个单独的请求拦截器
  request<T = any>(config: LWJRequestConfig): Promise<T> {
    if (!config.showLoading) {
      this.showLoading = false
    }
    return new Promise((resolve, reject) => {
      this.instance
        .request<any, LWJData<T>>(config)
        .then((res) => {
          resolve(res.data)
          this.showLoading = true
        })
        .catch((err) => {
          reject(err)
          this.showLoading = true
        })
    })
  }

  // 封装get请求
  get<T = any>(config: LWJRequestConfig): Promise<T> {
    return this.request({ ...config, method: 'GET' })
  }

  // 封装post请求
  post<T = any>(config: LWJRequestConfig): Promise<T> {
    return this.request({ ...config, method: 'POST' })
  }

  // 封装delete请求
  delete<T = any>(config: LWJRequestConfig): Promise<T> {
    return this.request({ ...config, method: 'DELETE' })
  }

  // 封装patch请求
  patch<T = any>(config: LWJRequestConfig): Promise<T> {
    return this.request({ ...config, method: 'PATCH' })
  }
}

export default LWJRequest

service/request/config.ts

// 1.区分环境变量方式一:
// export const API_BASE_URL = 'https://coderwhy/org/dev'
// export const API_BASE_URL = 'https://coderwhy/org/prod'

// 2.区分环境变量方式二:
// let baseURL = ''
// if (process.env.NODE_ENV === 'production') {
//   baseURL = 'https://coderwhy/org/prod'
// } else if (process.env.NODE_ENV === 'development') {
//   baseURL = 'https://coderwhy/org/dev'
// } else {
//   baseURL = 'https://coderwhy/org/test'
// }

// 3.区分环境变量方式三: 加载.env文件
export const API_BASE_URL = process.env.VUE_APP_BASE_URL

export const TIME_OUT = 5000

service/request/type.ts

export interface Result<T> {
  code: number
  data: T
}

service/index.ts

// 统一出口文件

import LWJRequest from "./request/request"
import { API_BASE_URL, TIME_OUT } from './request/config'
import localCache from '@/utils/cache'

const lwjRequest = new LWJRequest({
  baseURL: API_BASE_URL,
  timeout: TIME_OUT,
  // 可以让不同的类拥有不同的拦截器,更加灵活
  interceptorHooks: {
    // 请求成功拦截
    requestInterceptor: (config) => {
      const token = localCache.getCache('token')
      if (token && config.headers) {
        config.headers.Authorization = `Bearer ${token}`
      }
      return config
    },
    // 请求失败拦截
    requestInterceptorCatch: (err) => {
      return err
    },
    // 响应成功拦截
    responseInterceptor: (res) => {
      return res.data
    },
    // 响应失败拦截
    responseInterceptorCatch: (err) => {
      return err
    }
  }
})

// export const lwjRequest2 = new LWJRequest({
//   baseURL: '地址2'
// })

export default lwjRequest

service/login/login.ts

import lwjRequest from "../index";
import {IAccount,LoginInfo} from './type'

// 枚举
enum LoginAPI {
  AccountLogin = 'login',
  UserInfo = '/users/',
  UserMenus = '/role/'
}

/**
 * 登录
 * @param account 
 * @returns 
 */
export function accountLoginRequest(account: IAccount){
  return lwjRequest.post<LoginInfo>({
    url: LoginAPI.AccountLogin,
    data: account 
  })
}

/**
 * 根据id获取用户信息
 * @param id 
 * @returns 
 */
export function requestUserInfoById(id: number){
  return lwjRequest.get({
    url: LoginAPI.UserInfo + id,
  })
}

/**
 * 根据当前用户id去请求对应的菜单
 * @param id 
 * @returns 
 */
export function requestUserMenusByRoleId(id: number) {
  return lwjRequest.get({
    url: LoginAPI.UserMenus + id + '/menu'
  })
}

service/login/type.ts

export interface IAccount {
  name: string,
  password: string
}

export interface LoginInfo {
  id: number,
  name: string,
  token:string
}

// export interface IDataType<T = any> {
//   id: number,
//   token: T
// }

utils/cache.ts

// 封装本地存储方法
class LocalCache {
  setCache(key: string, value: any) {
    window.localStorage.setItem(key, JSON.stringify(value))
  }

  getCache(key: string) {
    const value = window.localStorage.getItem(key)
    if (value) {
      return JSON.parse(value)
    }
  }

  deleteCache(key: string) {
    window.localStorage.removeItem(key)
  }

  clearLocal() {
    window.localStorage.clear()
  }
}

export default new LocalCache()

方式二

service/config/index.ts

// 1.区分开发环境和生产环境
// export const BASE_URL = 'http://aaa.dev:8000'
// export const BASE_URL = 'http://aaa.prod:8000'

// 2.代码逻辑判断, 判断当前环境
// vite默认提供的环境变量
// console.log(import.meta.env.MODE)
// console.log(import.meta.env.DEV); // 是否开发环境
// console.log(import.meta.env.PROD); // 是否生产环境
// console.log(import.meta.env.SSR); // 是否是服务器端渲染(server side render)

let BASE_URL = "";
if (import.meta.env.PROD) {
  // 生产环境
  BASE_URL = "http://152.136.185.210:4000";
} else {
  // 开发环境
  BASE_URL = "http://152.136.185.210:5000";
}

// console.log(BASE_URL);

// 3.通过创建.env文件直接创建变量
// console.log(import.meta.env.VITE_URL);

export const TIME_OUT = 10000;
export { BASE_URL };

service/request/index.ts

import axios from "axios";
import type { AxiosInstance } from "axios";
import type { LWJRequestConfig } from "./type";

// 拦截器: 蒙版Loading/token/修改配置

/**
 * 两个难点:
 *  1.拦截器进行精细控制
 *    > 全局拦截器
 *    > 实例拦截器
 *    > 单次请求拦截器
 *
 *  2.响应结果的类型处理(泛型)
 */

class LWJRequest {
  instance: AxiosInstance;

  // request实例 => axios的实例
  constructor(config: LWJRequestConfig) {
    this.instance = axios.create(config);

    // 每个instance实例都添加拦截器
    this.instance.interceptors.request.use(
      (config) => {
        // loading/token
        return config;
      },
      (err) => {
        return err;
      }
    );
    this.instance.interceptors.response.use(
      (res) => {
        return res.data;
      },
      (err) => {
        return err;
      }
    );

    // 针对特定的LWJRequest实例添加拦截器
    this.instance.interceptors.request.use(
      config.interceptors?.requestSuccessFn,
      config.interceptors?.requestFailureFn
    );
    this.instance.interceptors.response.use(
      config.interceptors?.responseSuccessFn,
      config.interceptors?.responseFailureFn
    );
  }

  // 封装网络请求的方法
  // T => IHomeData
  request<T = any>(config: LWJRequestConfig<T>) {
    // 单次请求的成功拦截处理
    if (config.interceptors?.requestSuccessFn) {
      config = config.interceptors.requestSuccessFn(config);
    }

    // 返回Promise
    return new Promise<T>((resolve, reject) => {
      this.instance
        .request<any, T>(config)
        .then((res) => {
          // 单词响应的成功拦截处理
          if (config.interceptors?.responseSuccessFn) {
            res = config.interceptors.responseSuccessFn(res);
          }
          resolve(res);
        })
        .catch((err) => {
          reject(err);
        });
    });
  }

  get<T = any>(config: LWJRequestConfig<T>) {
    return this.request({ ...config, method: "GET" });
  }
  post<T = any>(config: LWJRequestConfig<T>) {
    return this.request({ ...config, method: "POST" });
  }
  delete<T = any>(config: LWJRequestConfig<T>) {
    return this.request({ ...config, method: "DELETE" });
  }
  patch<T = any>(config: LWJRequestConfig<T>) {
    return this.request({ ...config, method: "PATCH" });
  }
}

export default LWJRequest;

service/request/type.ts

import type { AxiosRequestConfig, AxiosResponse } from "axios";

// 针对AxiosRequestConfig配置进行扩展
export interface LWJInterceptors<T = AxiosResponse> {
  requestSuccessFn?: (config: AxiosRequestConfig) => AxiosRequestConfig;
  requestFailureFn?: (err: any) => any;
  responseSuccessFn?: (res: T) => T;
  responseFailureFn?: (err: any) => any;
}

export interface LWJRequestConfig<T = AxiosResponse>
  extends AxiosRequestConfig {
  interceptors?: LWJInterceptors<T>;
}

service/index.ts

import { LOGIN_TOKEN } from '@/global/constants'
import { localCache } from '@/utils/cache'
import { BASE_URL, TIME_OUT } from './config'
import LWJRequest from './request'

const lwjRequest = new LWJRequest({
  baseURL: BASE_URL,
  timeout: TIME_OUT,
  interceptors: {
    requestSuccessFn: (config) => {
      // 每一个请求都自动携带token
      const token = localCache.getCache(LOGIN_TOKEN)
      if (config.headers && token) {
        // 类型缩小
        config.headers.Authorization = 'Bearer ' + token
      }
      return config
    }
  }
})

export default lwjRequest

src/global/constants.ts

export const LOGIN_TOKEN = 'login/token'

src/utils/cache.ts

enum CacheType {
  Local,
  Session
}

class Cache {
  storage: Storage

  constructor(type: CacheType) {
    this.storage = type === CacheType.Local ? localStorage : sessionStorage
  }

  setCache(key: string, value: any) {
    if (value) {
      this.storage.setItem(key, JSON.stringify(value))
    }
  }

  getCache(key: string) {
    const value = this.storage.getItem(key)
    if (value) {
      return JSON.parse(value)
    }
  }

  removeCache(key: string) {
    this.storage.removeItem(key)
  }

  clear() {
    this.storage.clear()
  }
}

const localCache = new Cache(CacheType.Local)
const sessionCache = new Cache(CacheType.Session)

export { localCache, sessionCache }

service/login/index.ts

import hyRequest from '..'
import type { IAccount } from '@/types'
// import { localCache } from '@/utils/cache'
// import { LOGIN_TOKEN } from '@/global/constants'

export function accountLoginRequest(account: IAccount) {
  return hyRequest.post({
    url: '/login',
    data: account
  })
}

export function getUserInfoById(id: number) {
  return hyRequest.get({
    url: `/users/${id}`
    // headers: {
    //   Authorization: 'Bearer ' + localCache.getCache(LOGIN_TOKEN)
    // }
  })
}

export function getUserMenusByRoleId(id: number) {
  return hyRequest.get({
    url: `/role/${id}/menu`
  })
}
相关推荐
也无晴也无风雨1 小时前
深入剖析输入URL按下回车,浏览器做了什么
前端·后端·计算机网络
Martin -Tang2 小时前
Vue 3 中,ref 和 reactive的区别
前端·javascript·vue.js
FakeOccupational3 小时前
nodejs 020: React语法规则 props和state
前端·javascript·react.js
放逐者-保持本心,方可放逐3 小时前
react 组件应用
开发语言·前端·javascript·react.js·前端框架
曹天骄4 小时前
next中服务端组件共享接口数据
前端·javascript·react.js
阮少年、5 小时前
java后台生成模拟聊天截图并返回给前端
java·开发语言·前端
郝晨妤6 小时前
鸿蒙ArkTS和TS有什么区别?
前端·javascript·typescript·鸿蒙
AvatarGiser6 小时前
《ElementPlus 与 ElementUI 差异集合》Icon 图标 More 差异说明
前端·vue.js·elementui
喝旺仔la6 小时前
vue的样式知识点
前端·javascript·vue.js
别忘了微笑_cuicui6 小时前
elementUI中2个日期组件实现开始时间、结束时间(禁用日期面板、控制开始时间不能超过结束时间的时分秒)实现方案
前端·javascript·elementui