基础HTTP请求封装:axios实例创建、baseURL、请求与响应拦截器、超时设置
1. 原理与作用
在大型应用中,直接使用 axios 裸调用会导致大量重复配置(baseURL、headers、超时),且缺少统一的请求/响应处理管道。通过创建自定义 axios 实例,可以固化全局配置,并利用拦截器(interceptors)实现请求前/响应后的统一逻辑,例如:
- baseURL:将所有相对路径拼接为路径,便于环境切换。
- 请求拦截器:注入 、统一加时间戳、记录日志。
- 响应拦截器:统一解包数据、处理业务错误码、触发全局报错。
- 超时设置:防止死连接,提升用户体验。
| 配置项 | 作用 | 推荐值 |
|---|---|---|
baseURL | 接口基础路径,环境切换时只需改一处 | /api/v1 或完整域名 |
timeout | 请求超时时间(毫秒) | 10000 ~ 30000 |
headers | 默认请求头,如 Content-Type | { 'Content-Type': 'application/json' } |
withCredentials | 跨域携带 Cookie | true(需要 CORS 支持) |
2. 实现流程
- 安装 axios 依赖(npm / yarn)。
- 创建独立文件
request.js,导出自定义实例。 - 通过
axios.create()初始化实例,传入baseURL、timeout、headers等默认配置。 - 注册 请求拦截器(用
instance.interceptors.request.use):获取 localStorage 中的 ,添加到config.headers.Authorization。 - 注册 响应拦截器:对成功响应解包
response.data;对失败响应统一处理错误码(如 401 跳转登录)。 - 在业务模块中直接引用该实例,调用
request.get('/user')等。
关键点:拦截器必须返回
config(请求)或 response(响应),否则 Promise 链会中断。
3. 代码实现(标准封装)
以下是完整的 request.js 示例:
// request.js
import axios from 'axios'
// 1. 创建 axios 实例
const instance = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL || '/api',
timeout: 15000,
headers: { 'Content-Type': 'application/json' }
})
// 2. 请求拦截器
instance.interceptors.request.use(
config => {
const = localStorage.getItem('')
if () {
config.headers.Authorization = `Bearer ${}`
}
// 可选:加时间戳防缓存
config.params = { ...config.params, _t: Date.now() }
return config
},
error => Promise.reject(error)
)
// 3. 响应拦截器
instance.interceptors.response.use(
response => {
// 后端约定:res.data.code === 0 成功
const res = response.data
if (res.code !== 0) {
// 统一错误提示
ElMessage.error(res.msg || '请求失败')
return Promise.reject(new Error(res.msg))
}
return res.data // 解包后只返回业务数据
},
error => {
if (error.code === 'ECONNABORTED') {
ElMessage.error('请求超时,请检查网络')
} else if (error.response) {
const status = error.response.status
if (status === 401) {
localStorage.removeItem('')
window.location.href = '/login'
} else if (status === 500) {
ElMessage.error('服务器异常')
}
} else {
ElMessage.error('网络错误,请稍后重试')
}
return Promise.reject(error)
}
)
export default instance
业务组件中使用:
import request from '@/utils/request'
export function fetchUserList(params) {
return request.get('/user/list', { params })
}
4. 调试与验证
- 打印拦截器日志:在请求拦截器中
console.log('request config:', config);响应拦截器中console.log('response:', response),观察请求头、URL 是否正确。 - 模拟超时:将 timeout 设为
1,触发ECONNABORTED,检查错误提示。 - 断网测试:关闭服务器或网络,验证错误拦截器是否弹出“网络错误”。
- 跨域检查:在浏览器开发者工具 Network 面板查看请求域名是否与 baseURL 一致,CORS 头是否缺失。
调试技巧:使用
axios.interceptors.request.use 和 response.use 的第二个回调(onRejected)可以单独捕获拦截器本身的错误,避免被全局吞掉。
5. 常见错误与解决方案
| 错误现象 | 原因 | 解决 |
|---|---|---|
请求 URL 变成 http://localhost:3000http://.../api | baseURL 缺少尾随斜杠或请求路径 | baseURL 以 / 结尾,请求路径以 / 开头 |
| 拦截器中修改 config 不生效 | 没有返回修改后的 config | 确保 return config |
| 响应拦截器未捕获到业务错误码 | 后端返回 200 但 code != 0,未在 response 中处理 | 检查 response.data.code 逻辑 |
| 跨域请求失败(CORS) | 后端未设置 Access-Control-Allow-Origin | 后端配置或使用代理(vite proxy) |
| 超时后重复重试导致的堆栈 | 未限制重试次数 | 引入 axios-retry 并限制次数 |
6. 性能与稳定性优化
- 请求取消:使用
AbortController或CancelToken在组件卸载时取消未完成的请求。 - 连接池复用:浏览器自动管理 HTTP/1.1 连接池,仍可设置
keepAlive(axios 默认支持)。 - 避免重复请求:在请求拦截器中根据 URL + 参数做防抖(debounce),相同请求未完成时复用前一次 Promise。
- 日志采样:生产环境下关闭拦截器中的
console.log,改用指标采集。 - 超时动态调整:根据网络类型(WiFi / 4G)调整
timeout值。
注意:不要在响应拦截器中执行长时间同步操作(如 JSON.parse 大体积数据),避免阻塞主线程。大数据量建议在业务层处理。
7. 请求流程内部结构
下图展示了 axios 实例从创建到完成请求的完整生命周期,包含请求拦截、实际 HTTP 请求、响应拦截及错误处理分支:
8. 部署与多环境配置
推荐在 .env 文件中定义 VITE_API_BASE_URL,构建时自动注入:
# .env.development
VITE_API_BASE_URL = /api
# .env.production
VITE_API_BASE_URL = https://your-api.com/api/v1
若使用 webpack,可通过 DefinePlugin 注入 process.env.BASE_URL。拦截器中避免硬编码敏感信息,使用环境变量统一管理。
部署检查清单:
- 确保 baseURL 指向正确的生产 API 域名。
- 关闭所有
console.log调试输出。 - 响应拦截器的错误提示使用用户友好的文案。
- CORS 配置允许生产域名跨域。