在当今前端开发中,Fetch API 已取代 XMLHttpRequest 成为数据请求的标准方案。然而,许多团队在实际项目中仍面临一个共性痛点:每个组件或模块中反复编写相似的请求逻辑,错误处理散落各处,维护成本居高不下。如何构建一个可复用的 Fetch API 包装函数,并实现集中化的错误处理?这已成为提升前端工程化水平的关键一环。
痛点:散落的请求与重复的异常处理
使用原生 Fetch 时,开发者通常需要在每次调用中手动处理响应状态码、网络异常、JSON 解析错误等。例如,一个简单的 GET 请求就需要写:fetch(url).then(res => { if (!res.ok) throw new Error(res.status); return res.json(); }).catch(err => { /* 弹窗提示 */ })。当项目中有几十个接口时,类似的代码会重复出现,不仅冗余,还容易遗漏错误处理逻辑。一旦业务需求变更(如统一添加请求头、增加超时控制),开发者需要逐个文件修改,极易引发生产事故。
解决方案:构建统一请求层
业内最佳实践是创建一个名为 httpClient 或 apiClient 的包装模块,将所有通用逻辑封装在内。该模块对外暴露 get、post、put、delete 等方法,内部统一处理请求头拼接、超时控制、响应解析和错误降级。
以下是一个轻量级实现示例:
class HttpClient {
constructor(baseURL = '', defaultHeaders = {}) {
this.baseURL = baseURL;
this.defaultHeaders = defaultHeaders;
}
async request(method, url, data = null, options = {}) {
const config = {
method,
headers: { ...this.defaultHeaders, ...options.headers },
body: data ? JSON.stringify(data) : undefined,
signal: options.signal || null,
};
try {
const response = await fetch(`${this.baseURL}${url}`, config);
if (!response.ok) {
// 集中处理HTTP错误
const errorData = await response.json().catch(() => null);
throw new HttpClientError(
response.status,
errorData?.message || `请求失败 (${response.status})`
);
}
return await response.json();
} catch (error) {
if (error instanceof HttpClientError) throw error;
// 捕获网络异常(断网、超时等)
throw new HttpClientError(0, '网络连接异常,请稍后重试');
}
}
get(url, options) { return this.request('GET', url, null, options); }
post(url, data, options) { return this.request('POST', url, data, options); }
// ... 其他方法
}
class HttpClientError extends Error {
constructor(statusCode, message) {
super(message);
this.statusCode = statusCode;
}
}
该封装实现了三大核心设计:集中错误处理——所有 HTTP 状态码异常和网络异常均转换为自定义错误类型,业务代码只需捕获 HttpClientError;统一请求配置——baseURL 和默认 headers 只需实例化一次;可扩展性——通过 options 参数可透传 AbortSignal(用于取消请求)或自定义头部。
进阶:超时重试与认证集成
在实际工程中,包装函数还可进一步扩展。例如,借助 AbortController 实现请求超时控制:在 request 方法内创建 AbortController,并用 setTimeout 触发 abort();同时支持自动重试机制,当遇到网络波动时按指数退避策略重试 2~3 次。对于需要携带 Token 的鉴权场景,可在 defaultHeaders 中动态注入,并配合拦截器在 Token 过期时刷新或跳转登录。
最佳实践:让错误信息更友好
集中错误处理并不意味着“一刀切”的弹窗提示。建议将 HttpClientError 按状态码分门别类:401 应触发未授权逻辑,403 提示权限不足,500 则统一显示“服务暂不可用”。在实际业务中,还可将错误码和原始错误信息记录到监控平台(如 Sentry),同时通过回调函数或事件总线让不同页面自定义 UI 反馈。
总结:从“写代码”到“设计系统”
构建 Fetch 包装函数是前端工程化中的“最小投资,最大回报”。它不仅能消灭大量重复代码,更让团队形成统一的错误处理规范,降低后期维护心智负担。无论是初创项目的快速迭代,还是大型应用的长期演进,一个健壮的请求抽象层都不可或缺。正如一位资深前端架构师所言:“好的架构不是用来解决今天的问题,而是让明天的问题更容易解决。”现在就动手重构你的 Fetch 调用,让每一行请求代码都具备可复用、可观测、可维护的基因。