一、背景与需求
在日常开发中,我们频繁使用 fetch 来发起 HTTP 请求。然而,原生 fetch 代码往往较为冗长,不够灵活,且需要重复处理一些常见场景,例如:
- 设置默认请求头
- 自动处理 GET 请求的查询参数
- 处理请求体的序列化
- 统一的错误处理
为了解决这些问题,我封装了一个通用的 fetchApi 函数,以简化使用,并提高代码的复用性和可维护性。
二、代码实现
以下是封装后的 fetchApi 函数:
import { getGlobalData } from './data.js';
export default function fetchApi(url, options = {}) {
const defaultOptions = {
method: 'GET',
headers: {
'Content-Type': 'application/json;charset=utf-8',
},
dataType: 'json',
};
const mergedOptions = { ...defaultOptions, ...options };
// 正确处理 POST 和 PUT 请求的 body
if (['POST', 'PUT'].includes(mergedOptions.method) && typeof mergedOptions.body === 'object') {
mergedOptions.body = JSON.stringify(mergedOptions.body);
mergedOptions.headers['Content-Type'] = 'application/json';
}
let fullUrl = getGlobalData('baseUrlPrefix') + url;
if (mergedOptions.method === 'GET' && mergedOptions.params) {
const queryParams = new URLSearchParams(mergedOptions.params).toString();
fullUrl += (fullUrl.includes('?') ? '&' : '?') + queryParams;
}
return fetch(fullUrl, mergedOptions)
.then(response => {
if (response.ok) {
return mergedOptions.dataType === 'json' ? response.json() : response;
} else {
throw new Error(`HTTP 错误!状态码:${response.status}`);
}
})
.catch(error => {
console.error('网络错误:', error);
throw error;
});
}
三、功能分析
- 默认配置的设计:
defaultOptions提供了默认的 HTTP 请求方法、请求头以及数据类型,避免在每次调用时重复设置。- 使用
...语法合并用户自定义配置,使得默认值与自定义选项共存。
- 自动处理请求体:
- 对
POST和PUT请求,如果body是一个对象,会自动序列化为 JSON,并设置正确的Content-Type。
- 对
- 构造完整 URL:
getGlobalData('baseUrlPrefix')提供基础的 URL 前缀,保证所有请求可以统一管理。- GET 请求时,如果有
params,会自动将对象序列化为查询字符串并拼接到 URL。
- 统一的错误处理:
- 当响应状态码不在 200-299 范围内时,抛出错误。
- 捕获所有网络异常,并输出到控制台。
- 灵活的返回数据类型:
- 支持返回 JSON 格式,也可以直接返回原始响应对象。
四、使用示例
以下是几个典型的使用场景:
-
发起 GET 请求:
javascript复制代码fetchApi('/users', { params: { page: 1, size: 10 } }) .then(data => console.log('用户数据:', data)) .catch(error => console.error('请求失败:', error)); -
发起 POST 请求:
javascript复制代码fetchApi('/login', { method: 'POST', body: { username: 'admin', password: '123456' }, }) .then(data => console.log('登录成功:', data)) .catch(error => console.error('登录失败:', error)); -
发起 PUT 请求:
javascript复制代码fetchApi('/users/123', { method: 'PUT', body: { name: 'John Doe' }, }) .then(data => console.log('更新成功:', data)) .catch(error => console.error('更新失败:', error));
五、优点与改进方向
优点:
- 简洁易用: 将重复逻辑封装,减少了每次发请求时的代码量。
- 灵活扩展: 提供了
options参数,方便根据不同场景自定义请求。 - 错误处理统一: 提供了统一的错误处理机制,便于调试与维护。
- 模块化: 使用独立的函数封装,方便复用。
改进方向:
- 支持更多数据格式:
- 除了
json,可以扩展对其他格式(如text或blob)的支持。 - 在
dataType中添加更多选项,如dataType: 'text'。
- 除了
- 添加超时功能:
- 原生
fetch不支持超时,可以通过Promise.race和AbortController实现。
- 原生
- 更灵活的错误处理:
- 允许用户传入自定义的错误处理回调。
- 加入请求拦截器与响应拦截器:
- 类似于 Axios,可以在请求发送前或收到响应后执行一些额外的逻辑。
六、总结
封装的 fetchApi 函数实现了对原生 fetch 的增强,解决了许多实际开发中的痛点。它简化了 HTTP 请求逻辑,提高了代码的可读性和可维护性。未来可以通过添加超时功能和拦截器进一步优化,以满足更多复杂场景的需求。
通过本次封装,我更加熟悉了 fetch 的基本用法、JavaScript 的对象合并技巧以及异常处理的设计原则。