HTTP 请求封装
请求封装的重点,从来都不是“把 GET、POST 写成几个函数”。真正值得做的,是把业务里反复出现的共性问题收掉。
为什么项目里需要请求层
页面里如果直接写请求,代码通常很快会散掉:
- URL 到处都是
- token 逻辑到处都是
- 错误处理每页各写一套
- loading、重试、提示、权限失效逻辑越来越乱
所以项目里更稳的写法,一般会把请求层拆出来。
一个比较常见的分层
src/
api/
user.ts
order.ts
request/
client.ts
error.ts
auth.ts
types/
大致可以这么分:
client.ts:底层实例api/*.ts:业务接口error.ts:错误归一化auth.ts:token 和鉴权边界
底层实例该做什么
不管用 fetch 还是 axios,底层实例一般会做这些:
baseURL- timeout
- 默认 headers
- token 注入
- 统一错误处理
- 业务码处理
- 请求取消或重试基础能力
一个常见的 Axios 版本
import axios from 'axios'
export const http = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL,
timeout: 10000,
})
http.interceptors.request.use((config) => {
const token = localStorage.getItem('token')
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
return config
})
http.interceptors.response.use(
(response) => {
const res = response.data
if (res.code !== 0) {
throw new Error(res.message || 'request failed')
}
return res.data
},
(error) => {
return Promise.reject(error)
}
)
业务接口层怎么写
import { http } from '@/request/client'
export interface UserDetail {
id: string
name: string
}
export function getUserDetail(id: string) {
return http.get<UserDetail>(`/users/${id}`)
}
export function updateUserDetail(id: string, payload: Partial<UserDetail>) {
return http.put(`/users/${id}`, payload)
}
这样页面里拿到的是“用户接口函数”,而不是一堆底层请求细节。
常见封装点
1. 统一鉴权
- token 放哪里取
- 过期了怎么处理
- 刷新 token 要不要重放请求