vue3版本+TS(typescript)+简单封装api 配置反向代理
vue3版本+TS(typescript)+简单封装api 配置反向代理
一、背景与问题
在现代前端开发中,API调用和反向代理配置是核心需求。对于Vue3+TypeScript项目,直接使用原生fetch或axios存在以下痛点:
- 类型安全缺失:原始API调用缺少类型定义,导致运行时错误难以排查
- 重复代码:每个API请求都需要重复处理请求头、超时、错误处理等逻辑
- 跨域限制:开发环境需要配置反向代理解决跨域问题
- 环境差异:开发/生产环境需要不同的API地址和代理配置
本文将深入探讨如何通过TypeScript封装API接口,结合Vue3的组合式API特性,实现统一的请求管理,同时配置反向代理解决开发环境的跨域问题。
二、基本原理
1. TypeScript类型系统优势
TypeScript的类型系统可以为API接口提供强类型保障。通过定义接口类型,可以实现以下效果:
interface User {
id: number
name: string
email: string
}在API调用时,可以确保返回数据符合预期类型:
const user: User = await getUser(1)2. Axios封装原理
通过创建axios实例并配置统一参数,可以实现:
- 自动添加请求头(如Authorization)
- 统一错误处理
- 请求/响应拦截器
- 超时控制
3. 反向代理原理
开发环境使用反向代理解决跨域问题,核心原理是:
- 客户端请求 → 代理服务器 → 后端服务器
- 代理服务器转发请求并返回响应
- 避免浏览器同源策略限制
三、环境准备
1. 项目依赖
npm init -y
npm install -D typescript ts-node vite @vitejs/plugin-vue
npm install axios2. TypeScript配置
创建tsconfig.json:
{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"strict": true,
"moduleResolution": "node",
"esModuleInterop": true,
"skipLibCheck": true,
"outDir": "./dist",
"rootDir": "./src"
},
"include": ["src"]
}四、核心实现
1. API封装(核心代码)
创建src/api/index.ts:
// src/api/index.ts
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios'
// 定义请求配置类型
interface ApiConfig {
baseURL: string
timeout?: number
headers?: Record<string, string>
}
// 创建axios实例
const createApi = (config: ApiConfig): AxiosInstance => {
const api = axios.create(config)
// 请求拦截器
api.interceptors.request.use(
(config: AxiosRequestConfig) => {
// 添加请求头
config.headers = {
...config.headers,
'Content-Type': 'application/json',
'X-Requested-With': 'XMLHttpRequest'
}
return config
},
(error: any) => {
return Promise.reject(error)
}
)
// 响应拦截器
api.interceptors.response.use(
(response: AxiosResponse) => {
// 处理响应数据
return response.data
},
(error: any) => {
// 统一错误处理
const message = error.response?.data?.message || '服务器错误'
console.error('API Error:', message)
return Promise.reject(message)
}
)
return api
}
// 配置不同环境的API
export const api = {
development: createApi({
baseURL: 'http://localhost:3000/api',
timeout: 5000,
headers: {
'Authorization': 'Bearer dev_token'
}
}),
production: createApi({
baseURL: 'https://api.example.com',
timeout: 10000,
headers: {
'Authorization': 'Bearer prod_token'
}
})
}2. 反向代理配置(Vite开发服务器)
在vite.config.ts中配置代理:
// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { resolve } from 'path'
export default defineConfig({
plugins: [vue()],
resolve: {
alias: {
'@': resolve(__dirname, './src')
}
},
server: {
proxy: {
'/api': {
target: 'http://localhost:3000', // 后端服务地址
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
}
}
}
})3. 类型定义(接口规范)
创建src/types/api.ts:
// src/types/api.ts
export interface ApiResponse<T> {
code: number
message: string
data: T
}五、完整案例
1. 项目结构
project-root/
├── src/
│ ├── api/
│ │ └── index.ts
│ ├── types/
│ │ └── api.ts
│ └── main.ts
│ └── App.vue
├── vite.config.ts
├── tsconfig.json
└── package.json2. 使用示例(组件中调用API)
<!-- src/App.vue -->
<template>
<div>
<button @click="fetchData">获取数据</button>
<div v-if="data">{{ data.name }}</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { api } from '@/api'
const data = ref(null)
const fetchData = async () => {
try {
const res: ApiResponse<{ id: number, name: string }> = await api.development.get('/users/1')
data.value = res.data
} catch (error) {
console.error('请求失败:', error)
}
}
</script>3. 后端接口示例(假设后端服务)
// 后端示例(Node.js + Express)
app.get('/api/users/1', (req, res) => {
res.json({
code: 200,
message: '成功',
data: {
id: 1,
name: '张三'
}
})
})六、源码解析
1. API封装关键点
- 拦截器机制:通过拦截器统一处理请求和响应,避免重复代码
- 类型安全:使用泛型和类型断言确保数据类型正确
- 环境区分:根据环境选择不同的API地址和认证信息
2. 反向代理配置细节
changeOrigin: true:确保代理服务器能正确处理跨域请求rewrite函数:将/api路径重写为后端服务的根路径- 代理配置只在开发环境生效,生产环境应使用Nginx等专业服务器
七、进阶使用
1. 动态代理配置
在vite.config.ts中支持多环境配置:
export default defineConfig(({ mode }) => {
const proxyConfig = {
'/api': {
target: 'http://localhost:3000',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
}
}
if (mode === 'production') {
proxyConfig['/api'] = {
target: 'https://api.example.com',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
}
}
return {
// ...其他配置
server: {
proxy: proxyConfig
}
}
})2. 接口分类管理
按业务模块划分API接口:
// src/api/user.ts
export const getUser = (id: number) => api.development.get(`/users/${id}`)
export const createUser = (data: { name: string }) => api.development.post('/users', data)3. 请求重试机制
添加请求重试逻辑:
import { retry } from 'rxjs/operators'
api.interceptors.request.use(config => {
return retry(3, 1000)(config) // 重试3次,间隔1秒
})八、性能与工程实践
1. 性能优化
- 请求合并:通过
axios.all合并多个请求 - 缓存机制:对高频接口添加缓存
- 并发控制:使用
axios.CancelToken管理并发请求
2. 安全风险
- CORS配置:确保后端正确设置CORS头
- 敏感信息:避免在代理配置中暴露敏感信息
- HTTPS强制:生产环境强制使用HTTPS
3. 方案对比
| 方案 | 优点 | 缺点 |
|---|---|---|
| Vite代理 | 配置简单,开发便捷 | 仅适用于开发环境 |
| Nginx代理 | 支持生产环境,功能强大 | 配置复杂,需要额外部署 |
| 服务端代理 | 更安全,可控制请求 | 需要后端配合 |
九、常见问题与踩坑
1. 代理配置不生效
错误场景:
// 错误配置
proxy: {
'/api': 'http://localhost:3000'
}解决办法:
// 正确配置
proxy: {
'/api': {
target: 'http://localhost:3000',
changeOrigin: true
}
}2. 类型断言错误
错误场景:
const res = await api.get('/users/1') // 缺少类型定义解决办法:
const res: ApiResponse<{ id: number, name: string }> = await api.get('/users/1')3. 跨域请求失败
错误场景:
// 后端未配置CORS
res.setHeader('Access-Control-Allow-Origin', '*')解决办法:
// 后端配置CORS
res.setHeader('Access-Control-Allow-Origin', 'http://localhost:5000')十、最佳实践
- 统一接口管理:所有API请求通过统一的
api对象调用 - 类型安全:为每个接口定义明确的类型
- 环境区分:根据环境变量区分开发/生产环境
- 错误处理:统一的错误处理逻辑,避免重复代码
- 代理配置:开发环境使用Vite代理,生产环境使用Nginx
- 性能优化:对高频接口添加缓存,对慢接口添加重试机制
- 安全防护:生产环境强制使用HTTPS,配置CORS头
十一、总结
通过Vue3+TypeScript的API封装和反向代理配置,我们可以实现:
- 更安全的API调用
- 更高效的错误处理
- 更清晰的代码结构
- 更方便的环境管理
这种方案特别适合以下场景:
- 中小型项目需要快速搭建API调用体系
- 需要强类型保障的前端项目
- 开发环境需要解决跨域问题的项目
但需注意避免以下情况:
- 生产环境使用Vite代理(需部署Nginx)
- 在复杂业务场景中未合理划分API接口
- 忽略安全配置导致接口暴露
在实际开发中,建议结合项目规模和团队规范选择合适方案,合理使用TypeScript的类型系统和axios的拦截器机制,构建可维护、可扩展的API调用体系。
评论已关闭