本文详细解析在Vue3项目中基于TypeScript封装Axios的最佳实践。从src目录下的api、request、config及services四层架构设计入手,深入讲解请求拦截器、响应拦截器的配置,以及get/post等方法的封装。同时涵盖如何将API层统一挂载至Vue3全局属性,并配合Express搭建本地测试服务,实现前后端分离开发的高效协同与代码可维护性。
1.目录层级

在src目录下划分为5个主要文件夹。为简化处理,Axios相关的请求逻辑主要集中在api层、request层、config层以及services层中。
2.request层
该层级负责封装具体的请求逻辑,主要利用Axios提供的API进行封装,此处采用TypeScript进行代码编写。
2.1请求主体
//services.ts
import axios from "axios";
import type {
AxiosInstance,
} from "axios";
const service: AxiosInstance = axios.create({
baseURL: "http://localhost:3000",
timeout: 5000,
});
export default service
2.2拦截器
拦截器功能涵盖请求拦截器与响应拦截器两部分。在请求拦截器中,可以配置token、cookie等认证信息,并在请求头中执行各类前置操作。
//interceptors.ts
import type { AxiosError, AxiosRequestConfig, AxiosResponse } from "axios";
import { ElMessage } from "element-plus";
import service from "./srevice";
service.interceptors.request.use(
(config: AxiosRequestConfig) => {
//给请求头设置token
// if (token) {
// config.headers!.Authorization = `baseUrl ${token}`;
// }
return config;
},
(error: AxiosError) => {
ElMessage.error(error.message);
return Promise.reject(error);
}
);
/* 响应拦截器 */
service.interceptors.response.use(
(response: AxiosResponse) => {
const { code, message, data } = response.data; // 根据自定义错误码判断请求是否成功
if (code === 0) {
// 将组件用的数据返回
return data;
} else {
// 处理业务错误。
ElMessage.error(message);
return Promise.reject(new Error(message));
}
},
(error: AxiosError) => {
// 处理 HTTP 网络错误
let message = "";
// HTTP 状态码
const status = error.response?.status;
switch (status) {
case 401:
message = "token失效,请重新登录";
// 这里可以触发退出的 action
break;
case 403:
message = "没有权限,请获取权限后登录";
break;
case 404:
message = "页面不存在";
break;
case 500:
message = "服务器故障";
break;
case 502:
message = "数据库查询错误";
break;
default:
message = "网络连接错误";
}
ElMessage.error(message);
return Promise.reject(error);
}
);
export default service
2.3 封装请求方法
import type {AxiosRequestConfig } from "axios";
import service from "./interceptors";
const httpObj = {
get<T = any>(url: string, config?: AxiosRequestConfig): Promise<T> {
return service.get(url, config);
},
post<T = any>(
url: string,
data?: object,
config?: AxiosRequestConfig
): Promise<T> {
return service.post(url, data, config);
},
put<T = any>(
url: string,
data?: object,
config?: AxiosRequestConfig
): Promise<T> {
return service.put(url, data, config);
},
delete<T = any>(url: string, config?: AxiosRequestConfig): Promise<T> {
return service.delete(url, config);
},
};
export default httpObj;
3.api层
此层级旨在封装具体的API请求。开发中按页面模块功能进行划分,例如home页面的所有请求均置于home文件夹下,shop页面的请求则放在shop文件夹中。最后在index.ts中进行统一引入并导出。这种结构的优势在于,日常开发中能依据页面模块快速定位对应请求,且在团队协作中能有效避免接口调用的混乱。

3.1细分功能模块
//api/home/home.ts
import request from "../../request/index";
import services from "../../services/index";
let api = {
getTreeList: () => {
return request.get(services.treeUrl);
},
};
export default api;
3.2api层主体
//api/index.ts
import home from "./home/home"
let api = {
home: { ...home},
};
export default api;
4.service层
该层级主要用于对接口URL进行统一的模块化管理,其结构与api层类似,同样按照页面模块进行分层处理。

//services/home/home.ts
interface urlType {
treeUrl: string;
}
const url:urlType = {
treeUrl: "/tree/getTreeList",
};
export default url
//services/index.ts
import home from "./home/home"
export default {
...home
}
5.将api层请求挂载到全局中
在Vue3.0中,由于不再提供this上下文,因此无法像Vue2那样挂载到原型上,必须调用其提供的特定API来实现全局挂载。
//main.ts
import { createApp } from "vue";
import ElementPlus from "element-plus";
//引入elementui样式
import 'element-plus/dist/index.css'
import App from "./App.vue";
//引入api层
import api from "./api/index";
const app = createApp(App);
//挂载api层
app.config.globalProperties.$api = api;
app.use(ElementPlus);
app.mount("#app");
在实际页面使用时,需新建view文件夹存放页面组件,components文件夹存放公共组件,并在view下添加如home等具体组件文件。
//src/view/home.vue
<script setup lang="ts">
import { getCurrentInstance } from "vue";
//引入全局挂载变量
const { proxy }: any = getCurrentInstance();
//发送请求
const getTreeList = async (): Promise<void> => {
const data = await proxy.$api.home.getTreeList();
console.log(data);
};
//点击事件
const getList = (): void => {
getTreeList();
};
</script>
<template>
<el-button type="primary" @click="getList">点击</el-button>
</template>
<style scoped>
</style>
6.后端接口
此处使用的后端服务由Express编写,简单实现了路由层与请求服务层的分离。

db模块作为数据源,因未引入SQL数据库,故采用Node.js直接对JSON数据文件进行读写操作,以满足基本的增删改查需求。palyData模块专门用于操作数据JSON文件。
6.1 express搭建本地服务
//serve/index.js
const express = require('express')
const app = express()
const tree=require('./router/tree')
app.use(express.json())
app.use(express.urlencoded({ extended: false }))
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin','*');
// 允许的header类型
res.header('Access-Control-Allow-Headers','content-type');
// 跨域允许的请求方式
res.header('Access-Control-Allow-Methods','DELETE,PUT,POST,GET,OPTIONS');
if (req.method === 'OPTIONS') {
return res.send()
}
next()
})
app.use('/tree',tree)
app.listen(3000, () => {
console.log('3000端口服务启动')
})
6.2路由层封装
//serve/route/tree.js
const express = require('express')
const { readFile,writeFile} =require('../playData/playData')
const router=express.Router()
router.get('/getTreeList', (req, res) => {
readFile((data) => {
res.json(data)
})
})
router.get('/deleteTreeList/:id', (req, res) => {
let id = parseInt(req.params.id)
readFile((data) => {
let {
parent,
children
} = data
const newParent = parent.filter(item => item.id !== id)
const newChildren = children.filter(item => item.id !== id)
data.parent = newParent;
data.children = newChildren;
let json = JSON.stringify(data)
let msg='删除成功'
writeFile(json,res,msg)
})
})
router.post('/modifyTreeList', (req, res) => {
const {data}=req.body
let id=parseInt(data.id)
let name=data.name;
readFile((data) => {
let {
parent,
children
} = data
parent.forEach(item => {
if(item.id==id){
item.name=name
}
})
children.forEach(item => {
if(item.id==id){
item.name=name
}
})
data.parent = parent;
data.children = children;
let json = JSON.stringify(data)
let msg='修改成功'
writeFile(json,res,msg)
})
});
module.exports=router
6.3读写操作
//serve/playData/playData.js
const fs = require('fs')
const path = require('path')
const p=path.join(__dirname, '../db/index.json')
const readFile = (callBack) => {
fs.readFile(p, 'utf8', (err, data) => {
if (err) {
return
}
callBack && callBack(JSON.parse(data))
})
}
const writeFile=(json,res,msg)=>{
fs.writeFile(p, json, (err) => {
res.json({
msg
})
})
}
module.exports={
readFile,
writeFile
}
7.总结
本文主要对Axios在TypeScript环境下的封装进行了初步探讨。由于缺乏真实后端代码,无法进行完整测试,因此利用Node.js结合Express框架搭建了一个本地服务作为替代。在日常开发中,封装不仅是为了短期的便捷,良好的代码层级结构更有助于项目后期的迭代与维护,避免代码随着项目推进变得臃肿繁琐。优秀的代码应当是一目了然,而非让人难以理解。
以上就是vue3 typescript封装axios过程示例的详细内容,更多关于vue3 typescript封装axios的资料请关注本站其它相关文章!