大家好,我是你们的老朋友,一个每天都在和 Bug 斗智斗勇的前端程序员。今天我要给大家分享一个我最近写的“神器”——一个超级强大的 API 客户端工具。它不仅让我的工作效率翻倍,还让我的老板直呼“内行”!如果你也在为网络请求的复杂性头疼,那这篇文章你一定要看完!
正文:1. 为什么我要写这个工具?作为一个前端程序员,我每天都要和 API 打交道。GET、POST、PUT、DELETE……这些 HTTP 方法就像我的“老朋友”,但有时候它们也会让我抓狂。比如:
请求超时了怎么办?网络波动导致请求失败了怎么办?用户突然离开页面,未完成的请求怎么取消?重复请求同一个接口,能不能缓存一下?于是,我决定自己动手,写一个“万能”的 API 工具,解决这些问题!经过一番折腾,终于搞定了,效果还不错,老板看了直呼“内行”!
2. 这个工具到底有多牛?先来看看它的核心功能:
请求超时控制:再也不用担心请求卡死了,超时自动取消!请求重试机制:网络波动?不怕!自动重试,最多 3 次!请求取消功能:用户突然离开页面?一键取消未完成的请求!请求缓存:重复请求同一个接口?直接返回缓存结果,性能提升 100%!并发控制:同时发起太多请求?自动排队,避免资源耗尽!灵活的拦截器:想在请求前后加点“私货”?拦截器帮你搞定!是不是听起来就很厉害?别急,代码在后面,咱们慢慢看!
3. 代码展示:一看就懂,一学就会!以下是这个工具的完整代码(基于 JavaScript):
class Api{ constructor(baseUrl, headers = {}, maxConcurrentRequests = 5) { this.baseUrl = baseUrl; this.headers = { 'Content-Type': 'application/json', ...headers, }; this.requestInterceptors = []; this.responseInterceptors = []; this.maxConcurrentRequests = maxConcurrentRequests; this.currentRequests = 0; this.requestQueue = []; this.cache = new Map(); this.abortControllers = new Map(); } async request(endpoint, method, body = null, credentials = 'omit', timeout = 10000, retries = 3, useCache = false, customConfig = {}) { const cacheKey = `${method}:${endpoint}`; // 使用缓存 if (useCache && this.cache.has(cacheKey)) { return this.cache.get(cacheKey); } // 并发控制 if (this.currentRequests >= this.maxConcurrentRequests) { await new Promise((resolve) => this.requestQueue.push(resolve)); } this.currentRequests++; let lastError; for (let i = 0; i < retries; i++) { try { const result = await this._request(endpoint, method, body, credentials, timeout, customConfig); if (useCache) { this.cache.set(cacheKey, result); } return result; } catch (error) { lastError = error; if (i === retries - 1) { throw lastError; } } } } async _request(endpoint, method, body, credentials, timeout, customConfig) { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), timeout); // 存储 controller 以便后续取消 this.abortControllers.set(endpoint, controller); let config = { method, headers: this.headers, credentials, signal: controller.signal, ...customConfig, }; if (body) { config.body = JSON.stringify(body); } // 执行请求拦截器 for (let interceptor of this.requestInterceptors) { config = await interceptor(config, endpoint, method); } const url = `${this.baseUrl}${endpoint}`; let response; try { response = await fetch(url, config); if (!response.ok) { throw new ApiError(`HTTP 错误! 状态码: ${response.status}`, response.status, response, config); } } catch (error) { console.error('请求失败:', error); throw error; } finally { clearTimeout(timeoutId); this.abortControllers.delete(endpoint); this.currentRequests--; if (this.requestQueue.length > 0) { this.requestQueue.shift()(); } } // 执行响应拦截器 for (let interceptor of this.responseInterceptors) { response = await interceptor(response, endpoint, method); } return await response.json(); } async get(endpoint, queryParams = {}, credentials = 'omit', timeout = 10000, retries = 3, useCache = false) { let fullEndpoint = endpoint; if (Object.keys(queryParams).length > 0) { const queryString = new URLSearchParams(queryParams).toString(); fullEndpoint += `?${queryString}`; } return this.request(fullEndpoint, 'GET', null, credentials, timeout, retries, useCache); } async post(endpoint, body, credentials = 'omit', timeout = 10000, retries = 3) { return this.request(endpoint, 'POST', body, credentials, timeout, retries); } async put(endpoint, body, credentials = 'omit', timeout = 10000, retries = 3) { return this.request(endpoint, 'PUT', body, credentials, timeout, retries); } async patch(endpoint, body, credentials = 'omit', timeout = 10000, retries = 3) { return this.request(endpoint, 'PATCH', body, credentials, timeout, retries); } async delete(endpoint, credentials = 'omit', timeout = 10000, retries = 3) { return this.request(endpoint, 'DELETE', null, credentials, timeout, retries); } useRequestInterceptor(interceptor) { this.requestInterceptors.push(interceptor); } useResponseInterceptor(interceptor) { this.responseInterceptors.push(interceptor); } cancelRequest(endpoint) { if (this.abortControllers.has(endpoint)) { this.abortControllers.get(endpoint).abort(); this.abortControllers.delete(endpoint); } } clearCache() { this.cache.clear(); }}class ApiError extends Error{ constructor(message, statusCode, response, config) { super(message); this.statusCode = statusCode; this.response = response; this.config = config; }}export default Api;
4. 使用示例:简单到哭!const api = new Api('https://api.example.com', { Authorization: 'Bearer token' });// 添加请求拦截器api.useRequestInterceptor(async (config, endpoint, method) => { console.log(`请求拦截器: ${method} ${endpoint}`); return config;});// 发起 GET 请求(带缓存)api.get('/users', { page: 1 }, 'include', 10000, 3, true) .then(data => console.log(data)) .catch(error => console.error(error));// 发起 POST 请求api.post('/users', { name: 'John' }) .then(data => console.log(data)) .catch(error => console.error(error));
5. 总结:这个工具为什么值得你拥有?高效:解决网络请求中的各种痛点,提升开发效率。灵活:支持自定义配置、拦截器、缓存等功能。稳定:超时、重试、取消等机制,确保请求的稳定性。如果你觉得这个工具不错,欢迎点赞、转发、打赏!你的支持是我持续分享的动力!
结尾:好了,今天的分享就到这里。如果你觉得这篇文章对你有帮助,别忘了关注我,我会持续分享更多实用的技术干货!如果你有任何问题或建议,欢迎在评论区留言,我会一一回复!
求关注、求转发、求打赏!你们的支持是我最大的动力!
互动话题:你在开发中遇到过哪些网络请求的坑?欢迎在评论区分享你的故事!