Vue 3 实战:从页面到接口调用
Vue 3 实战:从页面到接口调用
前端最重要的工作之一是和后端接口交互。这篇说说 Vue 项目中怎么发请求、封装 API、管理错误。
安装 axios
pnpm add axios
封装请求工具
创建 src/utils/request.ts:
import axios from 'axios'
import type { AxiosInstance, AxiosError, InternalAxiosRequestConfig, AxiosResponse } from 'axios'
const request: AxiosInstance = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL || '/api',
timeout: 10000
})
// 请求拦截
request.interceptors.request.use(
(config: InternalAxiosRequestConfig) => {
// 添加 token
const token = localStorage.getItem('token')
if (token && config.headers) {
config.headers.Authorization = `Bearer ${token}`
}
return config
},
(error: AxiosError) => {
return Promise.reject(error)
}
)
// 响应拦截
request.interceptors.response.use(
(response: AxiosResponse) => {
return response.data
},
(error: AxiosError) => {
// 统一错误处理
if (error.response?.status === 401) {
localStorage.removeItem('token')
window.location.href = '/login'
}
return Promise.reject(error)
}
)
export default request
封装 API 模块
创建 src/api/user.ts:
import request from '../utils/request'
export interface User {
id: number
name: string
email: string
}
export const userApi = {
list(params?: { page: number; size: number }) {
return request.get<{ list: User[]; total: number }>('/users', { params })
},
get(id: number) {
return request.get<User>(`/users/${id}`)
},
create(data: Partial<User>) {
return request.post<User>('/users', data)
},
update(id: number, data: Partial<User>) {
return request.put<User>(`/users/${id}`, data)
},
delete(id: number) {
return request.delete(`/users/${id}`)
}
}
在组件中使用
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { userApi } from '../api/user'
import type { User } from '../api/user'
const users = ref<User[]>([])
const loading = ref(false)
const error = ref<string | null>(null)
async function fetchUsers() {
loading.value = true
error.value = null
try {
const res = await userApi.list({ page: 1, size: 10 })
users.value = res.list
} catch (e) {
error.value = '获取用户列表失败'
} finally {
loading.value = false
}
}
async function handleDelete(id: number) {
try {
await userApi.delete(id)
await fetchUsers() // 刷新列表
} catch {
alert('删除失败')
}
}
onMounted(fetchUsers)
</script>
<template>
<div>
<p v-if="loading">加载中...</p>
<p v-else-if="error">{{ error }}</p>
<ul v-else>
<li v-for="user in users" :key="user.id">
{{ user.name }} - {{ user.email }}
<button @click="handleDelete(user.id)">删除</button>
</li>
</ul>
</div>
</template>
封装组合式函数
把请求逻辑抽成可复用函数,创建 src/composables/useUser.ts:
import { ref } from 'vue'
import { userApi } from '../api/user'
import type { User } from '../api/user'
export function useUser() {
const users = ref<User[]>([])
const loading = ref(false)
const error = ref<string | null>(null)
async function fetchUsers() {
loading.value = true
error.value = null
try {
const res = await userApi.list({ page: 1, size: 10 })
users.value = res.list
} catch (e) {
error.value = '获取用户列表失败'
} finally {
loading.value = false
}
}
async function createUser(data: Partial<User>) {
await userApi.create(data)
await fetchUsers()
}
async function deleteUser(id: number) {
await userApi.delete(id)
await fetchUsers()
}
return {
users,
loading,
error,
fetchUsers,
createUser,
deleteUser
}
}
组件中使用更简洁:
<script setup lang="ts">
import { onMounted } from 'vue'
import { useUser } from '../composables/useUser'
const { users, loading, error, fetchUsers, deleteUser } = useUser()
onMounted(fetchUsers)
</script>
环境变量
创建 .env.development 和 .env.production:
# .env.development
VITE_API_BASE_URL=http://localhost:3000/api
# .env.production
VITE_API_BASE_URL=https://api.example.com
使用:import.meta.env.VITE_API_BASE_URL
总结
- axios 封装请求工具,统一处理拦截和错误
- API 模块化,按功能拆分
- 组合式函数封装复用逻辑
- 环境变量管理不同环境的配置
下篇是整个系列的最后一篇,做一个完整的列表页增删改查实战。
