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`
  })
}
相关推荐
weixin_382395236 小时前
为小工厂量身打造:本地部署的物料管理系统带缺料计算
前端·制造
__zRainy__6 小时前
解决pnpm v10+不自动构建
前端·pnpm·工程化
猫猫不是喵喵.7 小时前
Vue3 Props 属性
前端·javascript·vue.js
醉城夜风~8 小时前
CSS元素显示模式(display)
前端·css
AI大模型-小华8 小时前
Codex 三方充值快速入门指南
java·前端·数据库·chatgpt·ai编程·codex·chatgpt pro
做前端的娜娜子11 小时前
同一链接实现 PC Web 与移动 H5 自适应
前端·掘金·金石计划
小帅不太帅11 小时前
架构没变、规模没变,DeepSeek V4 Flash 正式版凭什么暴涨 47 分?
前端·aigc·deepseek
jarvisuni11 小时前
DeepSeekFlash前端依旧拉垮,而且变慢了很多!
前端·javascript·算法
卷福同学12 小时前
AI编程出海第二步:验证关键词能否做站
前端·人工智能·后端