Vite4+Typescript+Vue3+Pinia 从零搭建(7) - request封装
项目代码同步至码云 weiz-vue3-template
基于axios封装请求,支持多域名请求地址
安装
npm i axios
封装
utils 目录下新建 request 文件夹,并新建 index.ts、request.ts 和 status.ts 文件。
1. status.ts 文件主要是封装状态码
export const ErrMessage = (status: number | string): string => {
let message: string = ''
switch (status) {
case 400:
message = '请求错误!请您稍后重试'
break
case 401:
message = '未授权!请您重新登录'
break
case 403:
message = '当前账号无访问权限!'
break
case 404:
message = '访问的资源不存在!请您稍后重试'
break
case 405:
message = '请求方式错误!请您稍后重试'
break
case 408:
message = '请求超时!请您稍后重试'
break
case 500:
message = '服务异常!请您稍后重试'
break
case 501:
message = '不支持此请求!请您稍后重试'
break
case 502:
message = '网关错误!请您稍后重试'
break
case 503:
message = '服务不可用!请您稍后重试'
break
case 504:
message = '网关超时!请您稍后重试'
break
default:
message = '请求失败!请您稍后重试'
}
return message
}
此时,eslint会报 switch 前面的空格错误,需要修改 .eslintrc.cjs 里的 indent,修改后,错误消失。
rules: {
// Switch语句 https://zh-hans.eslint.org/docs/latest/rules/indent#switchcase
indent: ['error', 2, { SwitchCase: 1 }]
}
2. request.ts 主要是封装 axios
/**
* 封装axios
* axios 实例的类型为 AxiosInstance,请求需要传入的参数类型为 AxiosRequestConfig,响应的数据类型为 AxiosResponse,InternalAxiosRequestConfig 继承于 AxiosRequestConfig
*/
import axios, { AxiosInstance, AxiosRequestConfig, InternalAxiosRequestConfig, AxiosResponse } from 'axios'
import { ErrMessage } from './status'
// 自定义请求返回数据的类型
interface Data<T> {
data: T
code: string
success: boolean
}
// 扩展 InternalAxiosRequestConfig,让每个请求都可以控制是否要loading
interface RequestInternalAxiosRequestConfig extends InternalAxiosRequestConfig {
showLoading?: boolean
}
// 拦截器
interface InterceptorHooks {
requestInterceptor?: (config: RequestInternalAxiosRequestConfig) => RequestInternalAxiosRequestConfig
requestInterceptorCatch?: (error: any) => any
responseInterceptor?: (response: AxiosResponse) => AxiosResponse
responseInterceptorCatch?: (error: any) => any
}
// 扩展 AxiosRequestConfig,showLoading 给实例默认增加loading,interceptorHooks 拦截
interface RequestConfig extends AxiosRequestConfig {
showLoading?: boolean
interceptorHooks?: InterceptorHooks
}
class Request {
config: RequestConfig
instance: AxiosInstance
loading?: boolean // 用loading指代加载动画状态
constructor(options: RequestConfig) {
this.config = options
this.instance = axios.create(options)
this.setupInterceptor()
}
// 类型参数的作用,T决定AxiosResponse实例中data的类型
request<T = any>(config: RequestConfig): Promise<T> {
return new Promise((resolve, reject) => {
this.instance
.request<any, Data<T>>(config)
.then((res) => {
resolve(res.data)
})
.catch((err) => {
reject(err)
})
})
}
// 封装常用方法
get<T = any>(url: string, params?: object, _object = {}): Promise<T> {
return this.request({ url, params, ..._object, method: 'GET' })
}
post<T = any>(url: string, params?: object, _object = {}): Promise<T> {
return this.request({ url, params, ..._object, method: 'POST' })
}
delete<T = any>(url: string, params?: object, _object = {}): Promise<T> {
return this.request({ url, params, ..._object, method: 'DELETE' })
}
patch<T = any>(url: string, params?: object, _object = {}): Promise<T> {
return this.request({ url, params, ..._object, method: 'PATCH' })
}
put<T = any>(url: string, params?: object, _object = {}): Promise<T> {
return this.request({ url, params, ..._object, method: 'PUT' })
}
// 自定义拦截器 https://axios-http.com/zh/docs/interceptors
setupInterceptor(): void {
/**
* 通用拦截
*/
this.instance.interceptors.request.use((config: RequestInternalAxiosRequestConfig) => {
if (config.showLoading) {
// 加载loading动画
this.loading = true
}
return config
})
// 响应后关闭loading
this.instance.interceptors.response.use(
(res) => {
if (this.loading) this.loading = false
return res
},
(err) => {
const { response, message } = err
if (this.loading) this.loading = false
// 根据不同状态码,返回不同信息
const messageStr = response ? ErrMessage(response.status) : message || '请求失败,请重试'
window.alert(messageStr)
return Promise.reject(err)
}
)
/**
* 使用通用实例里的拦截,两个拦截都会生效,返回值以后一个执行的为准
*/
// 请求拦截
this.instance.interceptors.request.use(
this.config?.interceptorHooks?.requestInterceptor,
this.config?.interceptorHooks?.requestInterceptorCatch
)
// 响应拦截
this.instance.interceptors.response.use(
this.config?.interceptorHooks?.responseInterceptor,
this.config?.interceptorHooks?.responseInterceptorCatch
)
}
}
export default Request
3. index.ts 主要是创建 Request 实例
/**
* 创建实例,可以多个,当你需要请求多个不同域名的接口时
*/
import Request from './request'
import { getToken } from '@/utils/auth'
const defRequest = new Request({
// 这里用 Easy Mock 模拟了真实接口
baseURL: 'https://mock.mengxuegu.com/mock/65421527a6dde808a695e96d/official/',
timeout: 5000,
showLoading: true,
interceptorHooks: {
requestInterceptor: (config) => {
const token = getToken()
if (token) {
config.headers.Authorization = token
}
return config
},
requestInterceptorCatch: (err) => {
return err
},
responseInterceptor: (res) => {
return res.data
},
responseInterceptorCatch: (err) => {
return Promise.reject(err)
}
}
})
// 创建其他示例,然后导出
// const otherRequest = new Request({...})
export { defRequest }
使用
src 目录下新建 api 文件夹,并新建 login.ts
1. login.ts
import { defRequest } from '../utils/request'
export const loginApi = (params: any) => {
// 设置 showLoading,timeout 会覆盖index.ts里的默认值
return defRequest.post<any>('/login', params, { showLoading: false, timeout: 1000 })
}
2. 修改 login.vue
<script setup lang="ts">
import { ref } from 'vue'
import { storeToRefs } from 'pinia'
import { useUserStore } from '@store/user'
import { loginApi } from '@/api/login'
defineOptions({
name: 'V-login'
})
const userStore = useUserStore()
const { userInfo, token } = storeToRefs(userStore)
let userName = ref(userInfo.value.name)
let userToken = ref(token)
const updateUserName = () => {
userStore.setUserInfo({
name: userName.value
})
}
const updateUserToken = () => {
userStore.setToken(userToken.value)
}
const login = () => {
loginApi({
name: userName.value
})
.then((res) => {
userName.value = res.name
userToken.value = res.token
updateUserToken()
})
.catch((err) => {
console.log(err)
})
}
</script>
<template>
<div>login page</div>
name:
<input type="text" v-model="userName" @input="updateUserName" />
<br />
token:
<input type="text" v-model="userToken" />
<hr />
<button @click="login">login</button>
</template>
<style scoped></style>
点击 login 按钮,即可看到请求。

说明
对于 axios 的封装和使用,这里要说明几点:
1. 为什么要使用 InternalAxiosRequestConfig
axios 源码有修改,拦截器传入和返回的参数不再是 AxiosRequestConfig,而是这个新类型 InternalAxiosRequestConfig
想要具体了解,可以查看这篇博文 https://blog.csdn.net/huangfengnt/article/details/131490913
2. Request 里的 config 参数
constructor 里的 this.config 会接受所有实例参数,所以通用实例拦截里使用的是 this.config?.xxx
通用拦截里使用的是 config.showLoading,而不是 this.config.showLoading,是为了我们在实际的 api/login.ts 里可以再传入 showLoading,以满足我们单个请求的要求。而通过 this.config 里获取的配置是 request/index.ts 里传入的配置。在 config.showLoading 之前我们可以打印下这两个 config ,console.log(this.config, config) 结果如下:

如果在 login.ts 里不传入 showLoading,那么 config.showLoading 会去拿通用实例 request/index.ts 里的 showLoading。
** 当然如果不需要全局加载动画,整个 loading 也都可以去掉 **
3. 总结下 request/index.ts 和 api/login.ts 里的参数有什么不同
request/index.ts 里可以建多个实例,一般以 baseURL 来判断是否要多个,它的参数是当前url下的通用参数,拦截规则也是;
api/login.ts 是具体的请求,它的大部分参数是url和请求传参。同一个 baseURL 下有的请求有特殊的要求,那你就可以去加一些参数。
总的来说,request/index.ts 是对 baseURL 一样的请求的封装,request/request.ts 是对所有请求的封装
4. 优化
- 因为 Easy Mock 的接口支持跨域,所以没有配到代理里去,如果是正常开发接口,还需要修改
vite.config.ts里的proxy。不过我们之前的教程里已有代理配置说明,这里便不再赘述 baseURL还可以放在env变量里,以便区分开发环境和生产环境- ** 删除
loading,这里只是为了提供一种思路 **
Vite4+Typescript+Vue3+Pinia 从零搭建(7) - request封装的更多相关文章
- Vite-Admin后台管理系统|vite4+vue3+pinia前端后台框架实例
基于vite4.x+vue3+pinia前端后台管理系统解决方案ViteAdmin. 前段时间分享了一篇vue3自研pc端UI组件库VEPlus.这次带来最新开发的基于vite4+vue3+pinia ...
- 【vue3-element-admin 】基于 Vue3 + Vite4 + TypeScript + Element-Plus 从0到1搭建后台管理系统(前后端开源@有来开源组织)
vue3-element-admin 是基于 vue-element-admin 升级的 Vue3 + Element Plus 版本的后台管理前端解决方案,技术栈为 Vue3 + Vite4 + T ...
- 从零搭建TypeScript与React开发环境
前言 平时进行开发大多数是基于vue-cli或者create-react-app等官方或者公司内部搭建的脚手架. 我们业务仔做的最多就是npm i和npm run dev或者npm start,然 ...
- 从零搭建基于webpack的Electron-Vue3项目(1)——基于webpack的Vue3项目搭建
从零搭建基于webpack的Electron-Vue3项目(1)--基于webpack的Vue3项目搭建 前言 本篇文章内容,主要是基于webpack的Vue3项目开发环境进行搭建,暂时还不涉及到El ...
- 从零搭建react hooks项目(github有源代码)
前言 首先这是一个react17的项目,包含项目中常用的路由.状态管理.less及全局变量配置.UI等等一系列的功能,开箱即用,是为了以后启动项目方便,特地做的基础框架,在这里分享出来. 这里写一下背 ...
- 从零搭建基于golang的个人博客网站
原文链接 : http://www.bugclosed.com/post/14 从零搭建个人博客网站需要包括云服务器(虚拟主机),域名,程序环境,博客程序等方面.本博客 就是通过这几个环节建立起来的, ...
- SpringBoot整合Shiro实现基于角色的权限访问控制(RBAC)系统简单设计从零搭建
SpringBoot整合Shiro实现基于角色的权限访问控制(RBAC)系统简单设计从零搭建 技术栈 : SpringBoot + shiro + jpa + freemark ,因为篇幅原因,这里只 ...
- 从零搭建docker+jenkins 自动化部署环境
从零搭建docker+jenkins+node.js自动化部署环境 本次案例基于CentOS 7系统 适合有一定docker使用经验的人阅读 适合有一定linux命令使用经验的人阅读 1.docker ...
- TypeScript完全解读(26课时)_1.TypeScript完全解读-开发环境搭建
1.TypeScript完全解读-开发环境搭建 初始化项目 手动创建文件夹 D:\MyDemos\tsDemo\client-demo 用VSCode打开 npm init:初始化项目 然后我们的项目 ...
- 从零搭建consul
从零搭建consul 原文链接:https://blog.csdn.net/weixin_42107541/article/details/87640807#2linux_25 从零搭建consul1 ...
随机推荐
- 用Rust手把手编写一个Proxy(代理), 动工
用Rust手把手编写一个Proxy(代理), 动工 项目 ++wmproxy++ gitee 传送门 github 传送门 设计流程图 flowchart LR A[客户端] -->|Http| ...
- 文心一言 VS 讯飞星火 VS chatgpt (97)-- 算法导论9.3 3题
三.用go语言,假设所有元素都是互异的,说明在最坏情况下,如何才能使快速排序的运行时间为O(nlgn). 文心一言: 快速排序是一种基于分治策略的排序算法,其平均时间复杂度为O(nlgn).在最坏情况 ...
- Informix 4gl错误代码信息和更正
(一)Informix信息和更正 出版日期:6 1996 年 年 1 11 月 0 成功. 操作成功.当 SQL 语句成功地执行时,数据库服务器把这个 SQLCODE 值返回给应用程序. 100 没有 ...
- xgo多线程
import threading import time #导入xgoedu from xgoedu import XGOEDU from xgolib import XGO #导入xgolib # ...
- 4款.NET开源的Redis客户端驱动库
前言 今天给大家推荐4款.NET开源免费的Redis客户端驱动库(以下排名不分先后). Redis是什么? Redis全称是REmote DIctionary Service,即远程字典服务.Redi ...
- 探索CPU的黑盒子:解密指令执行的秘密
引言 在我们之前的章节中,我们着重讲解了CPU内部的处理过程,以及与之密切相关的数据总线知识.在这个基础上,我们今天将继续深入探讨CPU执行指令的相关知识,这对于我们理解计算机的工作原理至关重要. C ...
- 安装k8s
完成安装k8s集群以后,推荐使用https://github.com/512team/dhorse发布应用. 准备环境 准备两台服务器节点,如果需要安装虚拟机,可以参考<wmware和cento ...
- 一、docker的安装及docker-compose安装
一. 安装docker 1.1安装 curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun # https://get.d ...
- Java初始化顺序及使用Spring情况下初始化
Java初始化顺序 1 无继承情况下的Java初始化顺序: class Sample { Sample(String s) { System.out. ...
- Redis Functions 介绍之一
Redis提供了编程接口(programming interface)可以让你在Redis服务器端执行客户的脚本. 一个重大的变化就是从Redis 7开始,你可以选择使用Redis Functions ...