## 引言:为什么 Vue3 时代仍需要 Axios?
Vue3 带来了 Composition API、更好的 TypeScript 支持以及性能飞跃,但「如何优雅地发起 HTTP 请求」依旧是前端工程化的必答题。
官方推荐的 fetch API 在浏览器端足够轻量,却缺少「超时中断」「请求/响应拦截」「自动 JSON 序列化」等能力。Axios 凭借 110 kB 的体积与丰富的生态,仍是 Vue3 项目的事实标准。
本文将带你完成「基础配置 → 拦截器 → 错误处理 → TypeScript 类型推导 → 实战技巧」的闭环,并穿插展示 **TRAE IDE** 在「智能提示、代码片段、接口 Mock」等环节如何进一步提效,让网络请求代码写得快、跑得稳、可维护。
---
## 01|安装与最小可运行示例
### 1.1 安装依赖
```bash
# 推荐锁定 1.x 最新版,避免 0.x 的 breaking change
npm i axios@^1.7
# 若使用 TypeScript
npm i -D @types/node # 为 axios 内置类型补充 Node 全局变量1.2 全局挂载(可选)
Vue3 不再强制 Vue.prototype,官方推荐「按需导入」或「提供全局实例」。
以下给出两种主流姿势:
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 按需导入 | tree-shaking 友好,单元测试无依赖 | 每个文件都要 import | 组件库、微前端 |
| provide/inject 全局实例 | 模板中直接 $http | 需要额外 key 约定 | 后台管理系统 |
代码示例(provide/inject 版):
// plugins/axios.ts
import Axios from 'axios'
import type { App } from 'vue'
export const axiosKey = Symbol('axios')
export function install(app: App) {
const instance = Axios.create({
baseURL: import.meta.env.VITE_API_BASE, // 统一环境变量入口
timeout: 10_000,
headers: { 'X-Client': 'vue3' }
})
app.provide(axiosKey, instance)
}
// main.ts
import { createApp } from 'vue'
import App from './App.vue'
import { install } from './plugins/axios'
const app = createApp(App)
install(app)
app.mount('#app')组件内使用:
<script setup lang="ts">
import { inject } from 'vue'
import type { AxiosInstance } from 'axios'
import { axiosKey } from '@/plugins/axios'
const http = inject<AxiosInstance>(axiosKey)!
// 直接使用 http.get / post ...
</script>🎯 TRAE IDE 提示:输入
inject<AxiosInstance>时,补全列表会高亮类型定义,避免手写Symbol拼写错误;⌘ + 点击可一键跳转到axiosKey定义处。
02|拦截器:让「通用行为」只写一次
2.1 请求拦截器:自动携带 Token
// plugins/axios.ts
instance.interceptors.request.use(
config => {
const token = localStorage.getItem('access_token')
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
// 统一驼峰转下划线(视后端规范)
if (config.data && config.headers['Content-Type']?.includes('json')) {
config.data = humps.decamelizeKeys(config.data)
}
return config
},
error => Promise.reject(error)
)2.2 响应拦截器:统一包装体拆解
后端常返回:
{ "code": 0, "msg": "ok", "data": {...} }前端只想拿到 data,其余做统一提示:
instance.interceptors.response.use(
response => {
const { code, msg, data } = response.data
if (code === 0) return data
// 业务异常
ElMessage.error(msg) // ElementPlus
return Promise.reject(new Error(msg))
},
error => {
// HTTP 异常(超时、断网、500)
handleNetworkError(error)
return Promise.reject(error)
}
)03|错误处理:一张图理清「网络错误」与「业务错误」
stateDiagram-v2
[*] --> 发起请求
发起请求 --> 网络错误: 超时/断网/500
发起请求 --> 业务错误: 200 & code≠0
网络错误 --> 统一提示: 弹窗/通知栏
业务错误 --> 统一提示
统一提示 --> 写入日志: TRAE 问题面板
3.1 网络错误映射表
export function handleNetworkError(err: any): void {
const status: number = err.response?.status
switch (status) {
case 400:
ElMessage.error('请求参数异常')
break
case 401:
localStorage.clear()
router.replace('/login')
break
case 403:
ElMessage.warning('无权限操作')
break
case 404:
ElMessage.error('接口不存在')
break
case 500:
case 502:
ElMessage.error('服务器开小差')
break
default:
ElMessage.error('网络异常,请稍后重试')
}
}🎯 TRAE IDE 提示:在「问题面板」中可一键折叠 4xx/5xx 报错,点击行号直接跳转到
handleNetworkError,无需翻文件。
04|TypeScript:让 Axios 拥有「类型安全」的超能力
4.1 为接口定义泛型 DTO
// types/api.ts
export interface LoginDTO {
username: string
password: string
}
export interface UserVO {
id: number
username: string
avatar: string
}4.2 扩展 Axios 类型
// types/axios.d.ts
import 'axios'
declare module 'axios' {
export interface AxiosResponse<T = any> {
code: number
msg: string
data: T
}
}4.3 在组件中享受推导
async function login(form: LoginDTO) {
const user = await http.post<UserVO>('/login', form)
// user 自动推导为 UserVO,无需 as
avatarUrl.value = user.avatar
}