大家好,我是大华! 直到我把 原生 比如: 所以我们封装的目标就仨字:省事、统一。 每个请求都要写一遍错误处理,每个请求都要处理loading状态,每个请求都要判断状态码… 写到怀疑人生! 来看看封装后的使用方式: 是不是清爽多了?下面我来拆解这个封装是怎么做的。 基础封装:创建axios实例 文件上传需要不同的Content-Type,我们单独处理: 代码复用性:不用每个请求都写错误处理 这个 封装的关键思路: 你的项目中Axios是怎么封装的?欢迎在评论区分享交流~
在干前端时,每次写接口调用,没完没了的.then.catch,每个请求都要写一遍错误处理,重复代码写了一堆又一堆。
想写个防重复提交,页面数据都乱成一锅粥!Axios彻底封装了一遍。现在不仅代码清爽了,连后端同事都跑来问:“你这接口调用怎么写得这么优雅?”。1. 为啥要封装?不就是发个请求吗?
Axios是能用,但不好用。
2. 看看我之前写的屎山代码
// 以前的我:每个请求都要写一堆重复代码
axios.post('/api/submit', formData)
.then(response => {
if (response.data.success) {
alert('提交成功!')
} else {
alert('提交失败:' + response.data.message)
}
})
.catch(error => {
if (error.response.status === 401) {
alert('请先登录!')
router.push('/login')
} else if (error.response.status === 500) {
alert('服务器开小差了,请重试')
} else {
alert('网络异常,请检查网络连接')
}
})
.finally(() => {
this.loading = false
})3. 封装后
// 调用接口变得如此简单
const submitForm = async () => {
try {
const result = await api.post('/submit', formData, {
showSuccess: true, // 自动显示成功提示
retry: 3, // 失败自动重试3次
preventDuplicate: true // 防止重复提交
})
// 直接拿到业务数据,不用再解构response
console.log(result)
} catch (error) {
// 错误已经统一处理了,这里基本不用写代码
}
}4. 核心封装代码
// src/utils/request.js
import axios from 'axios'
import { message } from 'antd'
// 创建axios实例
const service = axios.create({
baseURL: process.env.REACT_APP_BASE_API,
timeout: 15000, // 超时时间
headers: {
'Content-Type': 'application/json;charset=utf-8'
}
})
// 存储pending请求
const pendingRequests = new Map()
// 生成请求的唯一key
const generateReqKey = (config) => {
const { url, method, params, data } = config
return [url, method, JSON.stringify(params), JSON.stringify(data)].join('&')
}
// 添加请求到pending池
const addPendingRequest = (config) => {
const key = generateReqKey(config)
config.cancelToken = config.cancelToken || new axios.CancelToken(cancel => {
if (!pendingRequests.has(key)) {
pendingRequests.set(key, cancel)
}
})
}
// 移除pending请求
const removePendingRequest = (config) => {
const key = generateReqKey(config)
if (pendingRequests.has(key)) {
pendingRequests.delete(key)
}
}
// 取消重复请求
const cancelPendingRequest = (config) => {
const key = generateReqKey(config)
if (pendingRequests.has(key)) {
const cancel = pendingRequests.get(key)
cancel('重复请求,自动取消')
pendingRequests.delete(key)
}
}5. 请求拦截器:统一处理
// 请求拦截器
service.interceptors.request.use(
(config) => {
// 取消重复请求
if (config.preventDuplicate !== false) {
cancelPendingRequest(config)
addPendingRequest(config)
}
// 显示loading
if (config.showLoading !== false) {
showLoading()
}
// 自动添加token
const token = localStorage.getItem('token')
if (token && config.needToken !== false) {
config.headers.Authorization = `Bearer ${token}`
}
return config
},
(error) => {
return Promise.reject(error)
}
)6. 响应拦截器:核心逻辑
// 响应拦截器
service.interceptors.response.use(
(response) => {
// 移除pending请求
removePendingRequest(response.config)
// 隐藏loading
hideLoading()
// 根据后端返回结构处理数据
const { data } = response
if (data.code === 200) {
// 自动显示成功提示
if (response.config.showSuccess) {
message.success(data.message || '操作成功')
}
return data
} else {
// 业务逻辑错误
message.error(data.message || '操作失败')
return Promise.reject(new Error(data.message))
}
},
async (error) => {
// 移除pending请求
removePendingRequest(error.config)
// 隐藏loading
hideLoading()
// 如果是重复请求被取消,不报错
if (axios.isCancel(error)) {
return Promise.reject(error)
}
// 重试机制
const { config } = error
if (config && config.retry > 0) {
config.retryCount = config.retryCount || 0
if (config.retryCount < config.retry) {
config.retryCount++
// 延迟重试
await new Promise(resolve => setTimeout(resolve, 1000))
return service(config)
}
}
// 统一错误处理
handleError(error)
return Promise.reject(error)
}
)
// 统一错误处理函数
const handleError = (error) => {
if (error.response) {
const { status } = error.response
switch (status) {
case 401:
message.error('未授权,请重新登录')
localStorage.removeItem('token')
window.location.href = '/login'
break
case 403:
message.error('拒绝访问')
break
case 404:
message.error('请求资源不存在')
break
case 500:
message.error('服务器内部错误')
break
default:
message.error('网络异常,请稍后重试')
}
} else if (error.request) {
message.error('网络连接失败,请检查网络')
} else {
message.error('请求发送失败')
}
}7. 文件上传特别处理
// 文件上传方法
export const uploadFile = async (url, file, options = {}) => {
const formData = new FormData()
formData.append('file', file)
return service.post(url, formData, {
headers: {
'Content-Type': 'multipart/form-data'
},
showLoading: true,
showSuccess: true,
...options
})
}
// 多文件上传
export const uploadMultipleFiles = async (url, files, options = {}) => {
const formData = new FormData()
files.forEach(file => {
formData.append('files', file)
})
return service.post(url, formData, {
headers: {
'Content-Type': 'multipart/form-data'
},
...options
})
}
// 使用示例
const handleUpload = async (file) => {
try {
const result = await uploadFile('/api/upload', file, {
onUploadProgress: (progressEvent) => {
const percent = Math.round(
(progressEvent.loaded * 100) / progressEvent.total
)
console.log(`上传进度: ${percent}%`)
}
})
console.log('上传成功', result)
} catch (error) {
console.error('上传失败', error)
}
}8. 实际使用示例
// src/api/user.js
import api from '@/utils/request'
// 用户登录
export const login = (data) => {
return api.post('/auth/login', data, {
showSuccess: true,
needToken: false // 登录接口不需要token
})
}
// 获取用户信息
export const getUserInfo = () => {
return api.get('/user/info', {
retry: 2, // 失败自动重试2次
preventDuplicate: true // 防止重复请求
})
}
// 提交表单数据
export const submitFormData = (formData) => {
return api.post('/form/submit', formData, {
showSuccess: true,
showLoading: true,
preventDuplicate: true, // 防止重复提交
retry: 3 // 网络错误时重试3次
})
}9. 在组件中的使用
import React, { useState } from 'react'
import { submitFormData, uploadFile } from '@/api/user'
const MyForm = () => {
const [loading, setLoading] = useState(false)
const handleSubmit = async (formData) => {
try {
setLoading(true)
const result = await submitFormData(formData)
console.log('提交成功', result)
} catch (error) {
// 错误已经统一处理,这里基本不用写代码
} finally {
setLoading(false)
}
}
const handleFileChange = async (event) => {
const file = event.target.files[0]
if (file) {
try {
const result = await uploadFile(file)
console.log('上传成功', result)
} catch (error) {
console.error('上传失败', error)
}
}
}
return (
<div>
{/* 表单内容 */}
<input type="file" onChange={handleFileChange} />
</div>
)
}总结
维护性:修改错误处理逻辑只需要改一个地方
用户体验:自动loading、错误提示、重试机制
网络优化:防止重复请求,减少服务器压力
团队协作:统一代码风格,降低沟通成本Axios封装现在已经成了我们团队的标配,新同事上手就能写出规范的接口调用代码。
本篇文章来源于微信公众号: 程序员刘大华
1 本站一切资源不代表本站立场,并不代表本站赞同其观点和对其真实性负责。
2 本站一律禁止以任何方式发布或转载任何违法的相关信息,访客发现请向站长举报
3 本站资源大多存储在云盘,如发现链接失效,请联系我们第一时间更新。











暂无评论内容