Vue中Axios二次封装
文章目录
- 安装配置 axios
- 安装 axios
- 配置 axios
- 创建 axios 实例
- 使用
- 请求
安装配置 axios
安装 axios
npm i axios -- save
配置 axios
创建目录 src/service/request
,在该目录下配置 axios
type.ts
文件
import type { AxiosRequestConfig, AxiosResponse } from 'axios'
// 1.定义拦截器的类型, T 是响应res.data的类型
export interface HYRequestInterceptors<T = any> {
requestInterceptor?: (config: AxiosRequestConfig) => AxiosRequestConfig
requestInterceptorCatch?: (error: any) => any
responseInterceptor?: (
res: AxiosResponse<T>
) => AxiosResponse<T> | Promise<AxiosResponse<T>>
responseInterceptorCatch?: (error: any) => any
}
export interface HYRequestConfig<T = any> extends AxiosRequestConfig {
// 2.这里可以扩展自己的类型
interceptors?: HYRequestInterceptors<T>
showLoading?: boolean
}
config.ts
文件
const BASE_URL = "http://localhost:8080/api";
const TIME_OUT = 10000
export { BASE_URL, TIME_OUT }
index.ts
文件
import axios from 'axios'
import type { AxiosInstance } from 'axios'
import type { HYRequestInterceptors, HYRequestConfig } from './type'
// 导入ElLoading组件和样式
import 'element-plus/es/components/loading/style/css'
import {ElLoading, ElMessage} from 'element-plus'
const DEFAULT_LOADING = true
class HYRequest<T = any> {
instance: AxiosInstance
interceptors?: HYRequestInterceptors // 指定拦截器的类型
showLoading?: boolean // 定义是否显示loading
loading?: any // loading的组件实例
constructor(config: HYRequestConfig<T>) {
// 创建axios实例
this.instance = axios.create(config)
// 默认是否显示加载进度
this.showLoading =
config.showLoading === undefined ? DEFAULT_LOADING : config.showLoading // 默认为true
// 从config中取出对应的实例的拦截器
this.interceptors = config.interceptors
// 如果某个实例的config中有定义拦截的回调函数,那么将这些函数添加到实例的拦截器中
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('所有的实例都有的拦截器: 请求成功拦截')
if (this.showLoading) {
this.loading = ElLoading.service({
lock: true,
text: '数据正在加载中....',
background: 'rgba(255, 255, 255, 0.7)',
fullscreen: false
})
}
return config
},
(err) => {
return err
}
)
this.instance.interceptors.response.use(
(res) => {
this.loading?.close() // 将loading移除
// 这里还可以对返回的数据进行判断
return res.data
},
(err) => {
this.loading?.close() // 将loading移除
// 例子: 判断不同的HttpErrorCode显示不同的错误信息
if(err && err.response){
const status = err.response.status
switch (status) {
case 400:
ElMessage.error("请求错误");
break;
case 401:
ElMessage.error("未授权, 请重新登录");
break;
case 403:
ElMessage.error("登录过期, 请重新登录");
break;
case 404:
ElMessage.error("请求错误, 未找到相应的资源");
break;
case 408:
ElMessage.error("请求超时");
break;
case 500:
ElMessage.error("服务器错误");
break;
case 504:
ElMessage.error("网络超时");
break;
default:
ElMessage.error("请求失败");
}
}
return err
}
)
}
request<T = any>(config: HYRequestConfig): Promise<T> {
return new Promise((resolve, reject) => {
// 判断某个请求是否需要显示loading
if (config.showLoading === false) {
this.showLoading = config.showLoading
}
this.instance
.request<T, T>(config)
.then((res) => {
// 3.将结果resolve返回出去
resolve(res)
})
.catch((err) => {
reject(err)
return err
})
.finally(() => {
// 将showLoading设置true, 这样不会影响下一个请求
this.showLoading = DEFAULT_LOADING
})
})
}
get<T = any>(config: HYRequestConfig<T>): Promise<T> {
return this.request<T>({ ...config, method: 'GET' })
}
post<T = any>(config: HYRequestConfig<T>): Promise<T> {
return this.request<T>({ ...config, method: 'POST' })
}
delete<T = any>(config: HYRequestConfig<T>): Promise<T> {
return this.request<T>({ ...config, method: 'DELETE' })
}
patch<T = any>(config: HYRequestConfig<T>): Promise<T> {
return this.request<T>({ ...config, method: 'PATCH' })
}
}
export default HYRequest
创建 axios 实例
在 src/service
下创建文件
inex.ts
文件
// service统一出口
import HYRequest from './request'
import { BASE_URL, TIME_OUT } from './request/config'
const http = new HYRequest({
baseURL: BASE_URL,
timeout: TIME_OUT,
// showLoading: true, // 全局配置显示loading
interceptors: {
requestInterceptor: (config) => {
// 携带token的拦截
const token = localStorage.getItem('token')
if (token && config.headers) {
config.headers.Authorization = `Bearer ${token}`
}
return config
},
requestInterceptorCatch: (err) => {
// console.log('单个实例-请求失败的拦截')
return err
},
responseInterceptor: (res) => {
// console.log('单个实例-响应成功的拦截')
return res
},
responseInterceptorCatch: (err) => {
// console.log('单个实例-响应失败的拦截')
return err
}
}
})
export default http
types.ts
文件
export interface IDataType<T = any>{
code: number
flag: boolean
data: T
}
使用
在 service
下创建目录,每个目录对应于使用的模块即可,比如这里使用 user
作为目录。
types.ts
export interface IUserInfo{
name: string
email: string
}
user.ts
import http from '../index'
import {IDataType} from "../types";
import {IUserInfo} from "./types";
enum UserAPI{
UserInfo = '/user/info'
}
export function requestUserInfo(){
return http.get<IDataType<IUserInfo>>({
url: UserAPI.UserInfo,
//showLoading: false // 是否需要显示 loading 样式
})
}
请求
一个 vue 文件
<template>
<div>
<h1>Axios 请求示例</h1>
<div v-if="userInfo">
<p>用户名: {{ userInfo.name }}</p>
<p>邮箱: {{ userInfo.email }}</p>
</div>
</div>
</template>
<script lang="ts" setup>
import {onMounted, reactive} from "vue";
import {requestUserInfo} from "./service/user/user";
let userInfo = reactive({name: '', email: ''});
onMounted(() => {
requestUserInfo().then(res => {
Object.assign(userInfo, res.data);
})
})
</script>
<style>
</style>