在前端开发中,我们经常需要与后端 API 进行交互。为了提高代码的可维护性、减少重复逻辑,并提升开发效率,对 axios 的请求进行合理的封装是非常有必要的。
本文将带你一步步了解如何通过一个通用的 to 函数,优雅地封装 axios 请求,实现:
- 统一处理业务异常;
- 简化异步调用流程;
- 避免层层嵌套的
if (res.code !== 0)判断; - 提高代码可读性和复用性。
🧩 背景问题:传统写法的痛点
假设你有一个接口调用如下:
js
深色版本
let res = await getMerchantOrder(data);
if (res.code != 0) {
showToast(res.msg);
return false;
}
这种写法虽然功能正常,但存在几个明显的问题:
- 每个请求都需要手动判断
res.code - 错误提示分散,不易统一管理
- 返回值结构不一致,不利于后续处理
- 难以集中处理网络层和业务层的错误
这会导致你的业务代码中充斥大量“防御性判断”,严重影响可读性。
✨ 解决方案:使用 to 函数统一处理请求结果
我们可以创建一个名为 to 的辅助函数,用于包装任何基于 Promise 的请求(如 axios),并返回一个标准格式的 [error, data] 结构。
🔨 实现代码如下:
ts
深色版本
/**
* 将 Promise 包装成 [error, data] 形式,简化异步操作错误处理
* @param {Promise} promise - 需要包装的 Promise 对象
* @param {*} errorExt - 可选,附加的错误信息对象
* @returns {Array} [error, data]
*/
export const to = async (promise, errorExt) => {
try {
const res = await promise;
// 如果业务状态码不是成功状态(例如 code !== 0)
if (res?.code !== 0) {
const errorMessage = res?.msg ?? '获取数据失败';
showToast(errorMessage);
return [new Error(errorMessage), null];
}
// 成功时返回 null 错误 + 数据
return [null, res.data];
} catch (err) {
// 捕获异常并返回 [error, null]
let parsedError = err;
if (errorExt) {
parsedError = Object.assign({}, err, errorExt);
}
return [parsedError, null];
}
};
💡 注意:这里的
showToast是你项目中已有的 UI 提示方法,比如uni.showToast、ElMessage或自定义 Toast 工具。
📦 使用方式:简洁又直观
现在你可以这样使用:
ts
深色版本
const [err, data] = await to(getMerchantOrder(data));
if (err) {
console.error('请求失败:', err.message);
return;
}
// 正常处理 data
console.log('订单数据:', data);
✅ 优势总结:
| 特性 | 描述 |
|---|---|
| ✅ 统一错误处理 | 所有错误都在 err 中返回 |
| ✅ 清晰结构 | 返回 [err, data],无需 try/catch 嵌套 |
| ✅ 减少冗余 | 不再需要 if (res.code !== 0) 处理 |
| ✅ 可扩展性强 | 支持自定义错误信息注入 |
🧪 示例场景对比
❌ 原始写法:
js
深色版本
async function fetchOrder() {
try {
const res = await getMerchantOrder(data);
if (res.code !== 0) {
showToast(res.msg);
return;
}
// do something with res.data
} catch (err) {
showToast('网络异常');
}
}
✅ 使用 to 后:
js
深色版本
async function fetchOrder() {
const [err, data] = await to(getMerchantOrder(data));
if (err) {
// 统一错误提示 + 日志记录
console.error('请求出错', err.message);
return;
}
// 直接处理 data
console.log('订单数据:', data);
}
是不是更加清爽了?
🧰 高级用法:结合 TypeScript 更加安全
如果你使用的是 TypeScript,可以为 to 添加类型支持:
ts
深色版本
type ResponseData<T> = {
code: number;
msg?: string;
data?: T;
};
export async function to<T>(promise: Promise<ResponseData<T>>, errorExt?: any): Promise<[Error | null, T | null]> {
try {
const res = await promise;
if (res.code !== 0) {
const msg = res.msg || '获取数据失败';
showToast(msg);
return [new Error(msg), null];
}
return [null, res.data as T];
} catch (err) {
const error = errorExt ? Object.assign({}, err, errorExt) : err;
return [error, null];
}
}
这样在 IDE 中就可以获得完整的类型提示和自动补全支持!
🧠 总结:让请求更优雅、更可控
通过对 Axios 请求的封装,我们实现了:
- 统一的错误处理机制;
- 标准化的数据结构返回;
- 更清晰的业务代码逻辑;
- 更强的可维护性和可测试性;
无论你是开发小程序、Web 应用,还是构建中后台系统,这样的封装都能极大提升开发体验和代码质量。
📚 扩展建议
如果你希望进一步增强这个封装工具,还可以考虑加入以下功能:
- 自动重试机制(如网络失败时 retry);
- 请求拦截器/响应拦截器;
- 全局错误上报;
- 接口 Mock 支持;
- 请求缓存策略;
- 支持取消请求(AbortController);
📌 最后提醒:
不要把所有请求都写得“千篇一律”,封装是手段,统一和规范才是目的。
如需我帮你生成一个完整的 Axios 封装模块(包含拦截器、TypeScript 支持、Mock 等),欢迎继续提问 👍
你可以将这篇文章发布到:
- 掘金 / CSDN / 博客园 / 知乎 / 微信公众号 / Notion / 内部 Wiki
- 或者作为团队编码规范的一部分共享给同事