Vue 3 中的 Axios 完全指南
本文档从零开始讲解在 Vue 3 项目里如何用 Axios 发 HTTP 请求:安装、基本用法、请求配置、响应与错误处理、封装实例、拦截器,以及在组件中的常见写法(列表、表单、加载与错误状态),配有大量示例,适合新手系统学习。
一、什么是 Axios?为什么在 Vue 里用它?
1.1 Axios 是什么?
Axios 是一个基于 Promise 的 HTTP 客户端,可以在浏览器和 Node.js 里发请求。
在 Vue 项目里,我们用它来请求后端接口:获取列表、提交表单、上传文件等。
它支持 GET、POST、PUT、PATCH、DELETE 等常见方法,支持 请求/响应拦截器、取消请求、超时 等,用起来比较方便。
1.2 和原生 fetch 的简单对比
- fetch:浏览器自带,需自己处理 JSON、错误码、超时等。
- Axios:第三方库,自动转 JSON、按状态码抛错、支持拦截器、API 更统一。
在 Vue 项目里,用 Axios 做接口请求非常常见。
1.3 安装
在项目根目录执行:
npm install axios
或:
yarn add axios
pnpm add axios
安装完成后,在代码里 import axios 即可使用。
二、最基础的用法
2.1 GET 请求(无参)
import axios from 'axios'
axios.get('https://api.example.com/users').then((res) => {
console.log(res.data)
}).catch((err) => {
console.error(err)
})
- axios.get(url) 返回一个 Promise。
- res 是响应对象,常用的是 res.data(后端返回的 JSON 会在这里)。
- 请求失败(网络错误、4xx/5xx)会进入 catch。
2.2 GET 请求(带查询参数)
写法一: 第二个参数传 params 对象,Axios 会自动拼成查询字符串。
axios.get('https://api.example.com/list', {
params: {
page: 1,
size: 10,
keyword: 'vue'
}
}).then((res) => {
console.log(res.data)
})
实际请求 URL 类似:https://api.example.com/list?page=1&size=10&keyword=vue。
写法二: 自己把参数拼在 URL 里。
axios.get('https://api.example.com/list?page=1&size=10').then((res) => {
console.log(res.data)
})
2.3 POST 请求(提交 JSON)
第三个参数是请求体,一般传对象,Axios 会按 JSON 发出去,并设置 Content-Type: application/json。
axios.post('https://api.example.com/login', {
username: 'admin',
password: '123456'
}).then((res) => {
console.log(res.data)
}).catch((err) => {
console.error(err)
})
2.4 PUT / PATCH / DELETE
axios.put('https://api.example.com/user/1', { name: '新名字' })
axios.patch('https://api.example.com/user/1', { age: 20 })
axios.delete('https://api.example.com/user/1')
用法和 get/post 类似:url、可选的 config(params、headers 等)、post/put/patch 的第二个参数是 data。
三、响应对象里有什么?
then 里拿到的 res 是 Axios 的响应对象,常用字段:
| 字段 | 含义 |
|---|---|
| res.data | 后端返回的响应体(通常是 JSON 解析后的对象或数组) |
| res.status | HTTP 状态码(如 200、404、500) |
| res.statusText | 状态文本(如 OK、Not Found) |
| res.headers | 响应头对象 |
组件里用到的,绝大多数是 res.data。
axios.get('/api/user').then((res) => {
console.log(res.data)
console.log(res.status)
})
四、错误处理(重要)
4.1 什么时候会进 catch?
- 网络错误(断网、跨域被拦、超时等):会进 catch,err 里没有 response 或 response 不完整。
- HTTP 状态码 4xx、5xx:Axios 默认也会抛错,会进 catch,此时 err.response 里有服务端返回的内容。
4.2 在 catch 里区分“网络错误”和“业务错误”
axios.get('/api/user').catch((err) => {
if (err.response) {
// 有 response 表示请求发到了服务器,服务器返回了 4xx/5xx
console.log('状态码:', err.response.status)
console.log('后端返回:', err.response.data)
} else {
// 没有 response:网络错误、超时、请求被取消等
console.log('网络或其它错误', err.message)
}
})
4.3 用 async/await 写(推荐)
用 async/await 时,用 try/catch 捕获错误:
async function fetchUser() {
try {
const res = await axios.get('/api/user')
console.log(res.data)
} catch (err) {
if (err.response) {
console.log('业务错误:', err.response.data)
} else {
console.log('网络错误:', err.message)
}
}
}
五、在 Vue 3 组件里发请求()
5.1 进入页面时请求列表(onMounted)
把请求写在 onMounted 里,用 ref 存列表和加载/错误状态,模板里根据状态显示“加载中”“错误”“列表”:
<template>
<div>
<p v-if="loading">加载中...</p>
<p v-else-if="error">{{ error }}</p>
<ul v-else>
<li v-for="item in list" :key="item.id">{{ item.name }}</li>
</ul>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import axios from 'axios'
const list = ref([])
const loading = ref(true)
const error = ref('')
onMounted(async () => {
try {
loading.value = true
error.value = ''
const res = await axios.get('https://api.example.com/list')
list.value = res.data
} catch (err) {
error.value = err.response?.data?.message || err.message || '请求失败'
} finally {
loading.value = false
}
})
</script>
- loading:请求前 true,请求结束(成功或失败)在 finally 里设为 false。
- error:失败时把提示文案赋给 error,模板里用 v-else-if=”error” 显示。
- list:成功时 list.value = res.data(按你后端实际结构可能是 res.data.list 等)。
5.2 点击按钮再请求(如提交表单)
<template>
<form @submit.prevent="onSubmit">
<input v-model="form.username" placeholder="用户名" />
<input v-model="form.password" type="password" placeholder="密码" />
<button type="submit" :disabled="submitting">提交</button>
</form>
</template>
<script setup>
import { ref, reactive } from 'vue'
import axios from 'axios'
const form = reactive({ username: '', password: '' })
const submitting = ref(false)
async function onSubmit() {
try {
submitting.value = true
const res = await axios.post('/api/login', form)
console.log('成功', res.data)
} catch (err) {
console.error(err.response?.data || err.message)
} finally {
submitting.value = false
}
}
</script>
- :disabled=”submitting” 防止重复提交。
- submitting 在 finally 里统一设为 false。
六、请求配置(baseURL、headers、timeout)
6.1 常用配置项
发请求时,第二个参数是 config 对象(GET 时第二个参数就是 config;POST 时第二个是 data,第三个是 config):
axios.get('/api/list', {
params: { page: 1 },
headers: { 'X-Token': 'xxx' },
timeout: 5000
})
axios.post('/api/login', { username: 'a', password: 'b' }, {
headers: { 'Content-Type': 'application/json' },
timeout: 5000
})
| 配置 | 含义 |
|---|---|
| params | 查询参数(GET 时常用) |
| data | 请求体(POST/PUT/PATCH 常用) |
| headers | 请求头 |
| timeout | 超时时间(毫秒) |
| baseURL | 基础 URL,和请求 url 拼在一起(见下一小节) |
6.2 创建实例(统一 baseURL 和 timeout)
项目里接口通常有统一的基础地址和超时时间,建议用 axios.create 创建一个实例,后面都用这个实例发请求:
// request.js 或 api/request.js
import axios from 'axios'
const request = axios.create({
baseURL: 'https://api.example.com',
timeout: 10000
})
export default request
使用:
import request from './request'
request.get('/users').then((res) => console.log(res.data))
request.post('/login', { username: 'a', password: 'b' })
- request.get(‘/users’) 实际请求:https://api.example.com/users。
- 若后端在本地:可设 baseURL: ‘/api’,再在开发环境用代理把
/api转到真实地址(见 Vite/Webpack 配置)。
七、拦截器(统一加 token、统一处理错误)
7.1 请求拦截器(例如统一加 Token)
在请求发出去之前统一加 token 或其它 header:
import axios from 'axios'
const request = axios.create({
baseURL: 'https://api.example.com',
timeout: 10000
})
request.interceptors.request.use(
(config) => {
const token = localStorage.getItem('token')
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
return config
},
(err) => {
return Promise.reject(err)
}
)
export default request
之后所有用 request 发的请求,都会自动带上 Authorization 头(若有 token)。
7.2 响应拦截器(统一处理 401、统一取 data)
在收到响应后、返回到 then 之前,可以统一处理状态码或统一只返回 data:
request.interceptors.response.use(
(res) => {
return res.data
},
(err) => {
if (err.response?.status === 401) {
localStorage.removeItem('token')
window.location.href = '/login'
}
return Promise.reject(err)
}
)
- 成功时:return res.data,这样在业务里 await request.get(‘/users’) 拿到的直接就是 data,不用再写 res.data。
- 失败时:401 可跳转登录;其它错误继续 Promise.reject(err),由业务里 catch 处理。
八、完整示例:封装 request + 在组件里用
8.1 封装 request(baseURL + 请求/响应拦截器)
// src/api/request.js
import axios from 'axios'
const request = axios.create({
baseURL: import.meta.env.VITE_API_BASE || '/api',
timeout: 10000
})
request.interceptors.request.use(
(config) => {
const token = localStorage.getItem('token')
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
return config
},
(err) => Promise.reject(err)
)
request.interceptors.response.use(
(res) => res.data,
(err) => {
if (err.response?.status === 401) {
localStorage.removeItem('token')
window.location.href = '/login'
}
return Promise.reject(err)
}
)
export default request
(若用 Vite,环境变量用 import.meta.env.VITE_XXX;baseURL 也可直接写字符串。)
8.2 封装接口函数(按模块)
// src/api/user.js
import request from './request'
export function getUser() {
return request.get('/user')
}
export function login(data) {
return request.post('/login', data)
}
8.3 在组件里使用
<template>
<div>
<p v-if="loading">加载中...</p>
<p v-else-if="error">{{ error }}</p>
<div v-else>{{ user?.name }}</div>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { getUser } from '@/api/user'
const user = ref(null)
const loading = ref(true)
const error = ref('')
onMounted(async () => {
try {
const data = await getUser()
user.value = data
} catch (err) {
error.value = err.response?.data?.message || '加载失败'
} finally {
loading.value = false
}
})
</script>
因为响应拦截器里 return res.data,所以 getUser() 的返回值就是 data。
九、常见场景示例
9.1 分页列表(params + 翻页)
const list = ref([])
const page = ref(1)
const total = ref(0)
async function loadList() {
try {
const data = await request.get('/list', {
params: { page: page.value, size: 10 }
})
list.value = data.list
total.value = data.total
} catch (err) {
console.error(err)
}
}
function nextPage() {
page.value++
loadList()
}
9.2 提交表单并跳转
async function onSubmit() {
try {
const data = await request.post('/register', form)
alert('注册成功')
router.push('/login')
} catch (err) {
alert(err.response?.data?.message || '注册失败')
}
}
9.3 删除并刷新列表
async function remove(id) {
if (!confirm('确定删除?')) return
try {
await request.delete(`/item/${id}`)
await loadList()
} catch (err) {
alert('删除失败')
}
}
十、易错点与注意点
10.1 接口返回结构要对应
后端可能是 { data: { list: [] } } 或 { list: [] },res.data 或拦截器返回后,要按实际结构取:res.data.list 或 data.list。
10.2 跨域与 baseURL
浏览器有同源策略,前端 http://localhost:5173 请求 https://api.xxx.com 会跨域。
开发时常用 代理:前端请求 /api/xxx,由 Vite/Webpack 转发到真实域名;此时 baseURL 可设为 ‘/api’。
10.3 别忘了 loading / error 和 finally
请求前设 loading = true,在 finally 里设 loading = false,避免请求一直转圈;错误信息赋给 error 并在模板里展示,体验更好。
十一、速查表
| 需求 | 写法 |
|---|---|
| GET | axios.get(url, { params }) 或 request.get(url, { params }) |
| POST | axios.post(url, data) |
| 拿响应体 | res.data(若拦截器 return res.data,则直接拿返回值) |
| 错误处理 | catch 或 try/catch,用 err.response?.status、err.response?.data |
| 统一 baseURL/timeout | axios.create({ baseURL, timeout }) |
| 统一加 token | 请求拦截器里改 config.headers.Authorization |
| 统一处理 401 | 响应拦截器里判断 err.response?.status === 401 再跳转登录 |
十二、学习建议
- 先会 get/post 和 res.data、catch 里区分 err.response 有无。
- 在组件里用 ref 存列表/loading/error,在 onMounted 或按钮事件里 async/await + try/catch。
- 项目里用 axios.create 建实例,配好 baseURL 和拦截器,接口按模块封装成函数再在组件里调用。
把本文档的“封装 request + 组件里请求列表”自己敲一遍,再改成你的接口地址和返回结构,会掌握得更牢。祝你学习顺利。