
做前端开发时,和后端接口打交道是家常便饭,Axios 作为常用的 http 客户端,把它封装成 api service 能让接口管理更规范、开发效率更高,但不少同学刚开始会犯愁:axios API service 到底咋搭建?怎么解决重复代码、错误处理这些问题?接下来用问答的方式,把 axiOS api service 从基础到实战的知识点拆明白,帮你把接口层理顺。
简单说,axIOS api service 是把 axios 的请求逻辑(像请求拦截、响应处理、错误捕获这些)做统一封装,再把每个接口的请求细节(url、参数、方法)单独管理的一套“接口工具集”。
直接用 axios 发请求的话,每个组件里可能都要写 axios.get('/xxx') 这类代码,一旦后端改了接口地址、请求头,就得满项目找地方改;而封装成 api service 后,所有接口配置集中在一个地方,拦截器统一处理鉴权、loading,甚至不同环境的 baseURL 切换也能一键搞定,举个例子:直接写请求时,登录接口可能在多个页面重复写 axios.post('/login', data);但用 api service 后,会封装成 Export function login(data) { return request.post('/login', data) },其他地方只要导入 login 函数调用就行,后期改接口路径只需要改 service 里的配置,维护起来省心多了。
怎么从零开始搭建一套 axios api service?
分步骤来讲,核心是“创建请求实例 + 配置拦截器 + 接口模块化管理”:
创建 axios 实例(统一配置基础参数)
新建 request.JS 文件,用 axios.create 初始化实例,通过环境变量区分开发/生产环境的 baseURL,还能设置超时时间、默认请求头等:
import axios from 'axios' const request = axios.create({ baseURL: import.meta.env.vite_API_BASEURL, // 用环境变量动态切换域名 timeout: 10000, // 请求超时时间(毫秒) headers: { 'Content-type': 'APPlication/json' } })
配置请求拦截器(处理通用请求逻辑)
请求发出去前,经常需要加 token、统一打开 loading、参数加密等,以“自动添加 token 到请求头”为例:
request.interceptors.request.use( (config) => { const token = localStorage.getItem('token') // 从本地存储取 token if (token) { config.headers.Authorization = `Bearer ${token}` // 加到请求头 } return config }, (error) => { return Promise.reject(error) } )
配置响应拦截器(处理通用响应逻辑)
拿到后端响应后,要处理业务错误码、剥离无效数据、关闭 loading、token 过期跳转等,统一处理业务错误码”:
request.interceptors.response.use(
(response) => {
const res = response.data
if (res.code !== 200) { // 假设后端约定 200 为成功码
// 业务错误:弹提示、跳登录等
return promise.reject(new Error(res.msg || '请求失败'))
}
return res // 只返回有效数据,简化业务层代码
},
(error) => {
// 网络错误、超时等处理
if (error.Code === 'ECONNABORTED') { // 超时错误
alert('请求超时啦,检查下网络~')
}
return PRomise.reject(error)
}
)接口模块化(按业务拆分接口)
把用户、商品、订单等接口按模块拆分到不同文件(如 user.js、goods.js),方便维护:
// user.js(用户模块接口)
import request from './request'
export function login(data) {
return request.post('/user/login', data)
}
export function getUserInfo() {
return request.get('/user/info')
}
// goods.js(商品模块接口)
export function getGoodslist(params) {
return request.get('/goods/list', { params })
}业务层调用(简洁复用)
在组件或逻辑层,直接导入接口函数调用,无需关心请求细节:
import { login } from '@/api/user'
async function handleLogin() {
try {
const res = Await login({ username: 'xxx', password: 'xxx' })
console.log(res) // 直接拿到处理后的数据
} catch (error) {
console.log('登录失败', error)
}
}请求拦截器和响应拦截器,在 api service 里有啥实际作用?
拦截器是 axios api service 的“灵魂”,能帮我们把通用逻辑收拢到一处,避免在每个接口里重复写代码。
请求拦截器:发请求前的“预处理”
鉴权自动化:像前面例子中,自动给请求头加 token,不用每个接口手动写;
全局 loading 管理:请求拦截时打开 loading,响应拦截时关闭(需处理多请求并发场景,避免提前关闭);
参数加密:对敏感参数(如密码)加密后再发送,降低传输风险;
动态切换域名:根据接口路径,临时切换 baseURL(比如部分接口需调用第三方服务)。
举个“全局 loading 管理”的例子(用请求计数避免多请求冲突):
let requestCount = 0 // 记录同时进行的请求数 request.interceptors.request.use((config) => { requestCount++ showLoading() // 显示全局 loading return config }) request.interceptors.response.use((response) => { requestCount-- if (requestCount === 0) { hideLoading() // 所有请求完成后关闭 } return response }, (error) => { requestCount-- if (requestCount === 0) { hideLoading() } return Promise.reject(error) })
响应拦截器:拿到响应后的“后处理”
剥离无效数据:后端返回一般是
{ code, msg, data },拦截器直接返回data,业务层无需重复写res.data;业务错误兜底:根据错误码(如 401 代表 token 过期),自动跳登录、弹提示;
数据解密:若后端返回加密数据,拦截器解密后再给业务层;
性能统计:记录请求开始和结束时间,计算接口耗时并上报。
接口请求失败了咋处理?还能做自动重试吗?
请求失败分业务错误(如参数错误、权限不足)和网络错误(如断网、超时、后端服务挂了),处理方式不同;自动重试适合“网络波动导致的临时失败”场景。
错误类型区分与基础处理
业务错误:由后端返回的错误码(如
code=500)触发,一般不重试,需提示用户(如“账号不存在”);网络错误:axios 走到
response拦截器的error分支,可通过error.message(如'Network Error')或error.code(如'ECONNABORTED'代表超时)判断。
自动重试方案
可借助 axios-retry 库,或自己写重试逻辑。
用 axios-retry 快速实现
安装依赖:npm i axios-retry,然后增强 axios 适配器:
import axios from 'axios' import retryAdapterEnhancer from 'axios-retry' const request = axios.create({ baseURL: '/api', // 其他配置... }) // 增强适配器,配置重试规则 const enhancedAdapter = retryAdapterEnhancer(axios.defaults.adapter, { retries: 3, // 重试次数 retryCondition: (error) => { // 仅对“网络错误”或“超时错误”重试 return ( axios.isNetworkError(error) || (error.code === 'ECONNABORTED' && error.message.includes('timeout')) ) } }) request.defaults.adapter = enhancedAdapter
自己写重试逻辑(更灵活)
在响应拦截器中判断错误类型,并重发请求(需处理“重试次数”和“退避策略”避免无限重试):
request.interceptors.response.use(null, (error) => { const { config } = error if (!config || !config.retry) return Promise.reject(error) // 接口未配置重试则直接拒绝 config.retryCount = config.retryCount || 0 // 记录已重试次数 if (config.retryCount >= config.retry) { // 超过最大重试次数 return Promise.reject(error) } config.retryCount++ // 退避策略:每次重试间隔递增(如 1s → 2s → 4s...) const delay = Math.pow(2, config.retryCount) * 1000 return new Promise((resolve) => { setTimeout(() => { resolve(request(config)) // 重新发起请求 }, delay) }) }) // 业务层调用时,给接口配置 retry 参数 export function getGoodsList(params) { return request.get('/goods/list', { params, retry: 3 // 该接口允许重试 3 次 }) }
多个接口并发请求咋处理?axios 里有啥技巧?
日常开发中,“页面初始化时同时请求用户信息、菜单、购物车”这类并发请求很常见,axios 结合原生 Promise 能高效处理。
全成功才继续:Promise.all
适合“所有接口都成功,页面才渲染”的场景,只要有一个接口失败,整体进入 catch:
import { getUserInfo, getmenuList, getCart } from '@/api'
Async function initPage() {
try {
const [userRes, menuRes, cartRes] = await Promise.all([
getUserInfo(),
getMenuList(),
getCart()
])
// 三个接口都成功,再处理数据
console.log(userRes, menuRes, cartRes)
} catch (error) {
console.log('初始化失败', error)
}
}不管成功失败都要结果:Promise.allSettled
适合“部分接口失败不影响页面核心功能”的场景,会返回所有请求的“成功/失败”状态:
async function initPage() {
const results = awAIt Promise.allSettled([
getUserInfo(),
getMenuList(),
getCart()
])
results.forEach((result) => {
if (result.status === 'fulfilled') {
console.log('成功', result.value)
} else {
console.log('失败', result.reason)
}
})
}按顺序依赖请求:async/await 链式调用
适合“前一个接口的结果是后一个接口的参数”的场景(如先拿用户 ID,再查订单):
async function getOrderInfo() {
const user = await getUserInfo() // 先请求用户信息
const order = await getOrderByUserId(user.id) // 用用户 ID 请求订单
return order
}怎么给 axios api service 加缓存?避免重复请求相同接口?
缓存能减少重复请求、提升页面性能,分“拦截重复请求”和“数据缓存”两种场景。
拦截重复请求(避免短时间内重复发相同请求)
在请求拦截器中,记录“正在请求的接口(url + 参数)”,若重复则取消之前的请求:
const pendingRequests = new map() // 存储待处理的请求 // 生成唯一 key(method + url + 参数) function generateKey(config) { return [config.method, config.url, JSON.stringify(config.params), JSON.stringify(config.data)].join('&') } request.interceptors.request.use((config) => { const key = generateKey(config) // 若有相同请求正在pending,取消它 if (pendingRequests.has(key)) { const cancelToken = pendingRequests.get(key) cancelToken.cancel('重复请求,已取消') pendingRequests.delete(key) } // 为当前请求创建 CancelToken config.cancelToken = new axios.CancelToken((cancel) => { pendingRequests.set(key, { cancel }) }) return config }, (error) => { return Promise.reject(error) }) request.interceptors.response.use((response) => { const key = generateKey(response.config) pendingRequests.delete(key) // 请求完成,从 pending 中移除 return response }, (error) => { if (axios.isCancel(error)) { console.log('请求被取消', error.message) } else { const key = generateKey(error.config) pendingRequests.delete(key) } return Promise.reject(error) })
数据缓存(重复请求直接读缓存)
适合“数据不常变化”的接口(如商品分类、静态配置),把响应数据存在内存或 localStorage:
const CAChe = new Map() // 内存缓存 const CACHE_EXPIRE = 60 * 1000 // 缓存有效期(1分钟) request.interceptors.request.use((config) => { if (config.method !== 'get') return config // 只缓存 get 请求 const key = generateKey(config) const cached = cache.get(key) // 若缓存存在且未过期,直接返回缓存数据 if (cached && date.now() - cached.timestamp < CACHE_EXPIRE) { return Promise.resolve(cached.data) } return config }, (error) => { return Promise.reject(error) }) request.interceptors.response.use((response) => { if (response.config.method === 'get') { const key = generateKey(response.config) cache.set(key, { data: response, timestamp: Date.now() }) } return response }, (error) => { return Promise.reject(error) })
前后端分离项目里,axios api service 咋和后端配合?
前后端分离后,“接口文档”是协作核心,axios api service 能让双方并行开发、高效联调。
先定文档,再并行开发
后端先输出接口文档(如 Swagger、Yapi),定义好 url、请求方法、参数格式、响应结构,前端根据文档,在 api service 里写好接口定义,并用 Mock 数据做前端页面。
以 Yapi 里的“登录接口”为例:
URL:
/user/loginMethod:
POST请求参数:
{ username: String, password: string }响应:
{ code: number, msg: string, data: { token: string } }
前端可先用 Mock.js 模拟响应:
import Mock from 'mockjs'
// 拦截登录接口,返回模拟数据
Mock.mock('/user/login', 'post', (req) => {
const { username, password } = JSON.parse(req.body)
if (username === 'mock' && password === 'mock') {
return { code: 200, msg: '登录成功', data: { token: Mock.RanDOM.gUId() } }
} else {
return { code: 400, msg: '账号密码错误' }
}
})这样前端能独立开发页面,后端同时开发真实接口,联调时只需切换 baseURL 到后端服务地址,无需修改接口调用代码。
联调时的问题排查
联调阶段,在拦截器中打印请求详情(url、参数、耗时),快速定位问题:
request.interceptors.request.use((config) => {
console.log('请求发送:', config.url, config.params || config.data)
config.requestStartTime = Date.now() // 记录请求开始时间
return config
})
request.interceptors.response.use((response) => {
const耗时 = Date.now() - response.config.requestStartTime
console.log('响应接收:', response.config.url, response.data, '耗时:', 耗时, 'ms')
return response
}, (error) => {
console.log('请求错误:', error.config.url, error.message)
return Promise.reject(error)
})后端返回不符合预期时,通过控制台的请求参数和响应数据,能快速判断是“前端传参错误”还是“后端逻辑错误”。
和 TypeScript 结合,axios api service 能玩出啥花样?
Typescript 能给 axios api service 加上强类型约束,减少传参错误、响应解析错误等问题,提升代码健壮性。
给请求参数和响应数据加类型
定义接口的请求参数和响应的 TypeScript 类型,让编辑器自动提示、报错:
// types/user.d.ts(类型定义文件) export interface LoginParams { username: string password: string } export interface








网友评论文明上网理性发言 已有0人参与
发表评论: