罗源县建设局网站深圳北网站建设
2026/3/29 18:34:13 网站建设 项目流程
罗源县建设局网站,深圳北网站建设,做硅胶的网站,上饶seo博客从零构建一个轻量级axios替代方案#xff1a;揭秘XHR封装与拦截器设计 1. 为什么需要自建HTTP请求库#xff1f; 在当今前端开发中#xff0c;HTTP请求库就像空气一样无处不在。axios作为目前最流行的选择#xff0c;确实提供了强大的功能和良好的开发体验。但你是否想过揭秘XHR封装与拦截器设计1. 为什么需要自建HTTP请求库在当今前端开发中HTTP请求库就像空气一样无处不在。axios作为目前最流行的选择确实提供了强大的功能和良好的开发体验。但你是否想过当面试官问你axios是如何工作的时除了说它很好用还能给出什么深度回答构建自己的HTTP请求库有几个实际价值深入理解底层机制通过手写实现你会真正明白XMLHttpRequest(XHR)的工作原理定制化需求商业项目可能有特殊需求如独特的认证流程或日志记录性能优化去除不需要的功能打造更轻量的解决方案面试加分展示对底层原理的理解而不只是API调用我曾在一个性能敏感的项目中发现axios的体积对首屏加载造成了压力。通过自建精简版请求库最终减少了约40%的包体积。2. 基础XHR封装打造核心请求功能2.1 XMLHttpRequest基础现代浏览器都内置了XMLHttpRequest对象它是所有AJAX请求的基石。让我们先看一个最简单的GET请求示例const xhr new XMLHttpRequest(); xhr.open(GET, https://api.example.com/data, true); xhr.onreadystatechange function() { if (xhr.readyState 4) { if (xhr.status 200 xhr.status 300) { console.log(JSON.parse(xhr.responseText)); } else { console.error(Request failed); } } }; xhr.send();这段代码虽然简单但已经包含了HTTP请求的核心要素请求方法、URL、异步处理和响应处理。2.2 Promise封装现代前端开发中Promise已经成为异步处理的标准。让我们将上面的代码用Promise包装function request(config) { return new Promise((resolve, reject) { const xhr new XMLHttpRequest(); xhr.open(config.method || GET, config.url, true); xhr.onreadystatechange function() { if (xhr.readyState 4) { if (xhr.status 200 xhr.status 300) { resolve({ data: JSON.parse(xhr.responseText), status: xhr.status, statusText: xhr.statusText }); } else { reject(new Error(Request failed with status ${xhr.status})); } } }; xhr.send(config.data ? JSON.stringify(config.data) : null); }); }现在我们可以这样使用request({ url: https://api.example.com/data, method: GET }).then(response { console.log(response.data); }).catch(error { console.error(error); });2.3 支持多种请求方法我们的库应该支持常见的HTTP方法GET、POST、PUT、DELETE等。关键区别在于GET/DELETE参数通过URL传递POST/PUT参数通过请求体传递function request(config) { return new Promise((resolve, reject) { const xhr new XMLHttpRequest(); let url config.url; const method (config.method || GET).toUpperCase(); // 处理GET/DELETE参数 if (method GET || method DELETE) { if (config.params) { const params new URLSearchParams(); Object.keys(config.params).forEach(key { params.append(key, config.params[key]); }); url ?${params.toString()}; } } xhr.open(method, url, true); // 设置POST/PUT请求头 if (method POST || method PUT) { xhr.setRequestHeader(Content-Type, application/json); } xhr.onreadystatechange function() { if (xhr.readyState 4) { if (xhr.status 200 xhr.status 300) { resolve({ data: JSON.parse(xhr.responseText), status: xhr.status, statusText: xhr.statusText }); } else { reject(new Error(Request failed with status ${xhr.status})); } } }; xhr.send(method POST || method PUT ? JSON.stringify(config.data) : null); }); }3. 拦截器系统设计拦截器是axios最强大的功能之一它允许我们在请求发出前和响应返回后插入处理逻辑。典型的应用场景包括统一添加认证头请求/响应数据转换错误统一处理日志记录3.1 拦截器基本结构拦截器本质上是一个中间件系统包含请求拦截器和响应拦截器。我们可以用数组来存储这些拦截器class MiniAxios { constructor() { this.interceptors { request: [], response: [] }; } useRequestInterceptor(fulfilled, rejected) { this.interceptors.request.push({ fulfilled, rejected }); } useResponseInterceptor(fulfilled, rejected) { this.interceptors.response.push({ fulfilled, rejected }); } }3.2 实现拦截器链拦截器的执行顺序是请求拦截器 → 发送请求 → 响应拦截器。我们可以用Promise链来实现class MiniAxios { // ...之前的代码 request(config) { // 初始配置 let promise Promise.resolve(config); // 请求拦截器 this.interceptors.request.forEach(interceptor { promise promise.then(interceptor.fulfilled, interceptor.rejected); }); // 发送请求 promise promise.then(this.dispatchRequest); // 响应拦截器 this.interceptors.response.forEach(interceptor { promise promise.then(interceptor.fulfilled, interceptor.rejected); }); return promise; } dispatchRequest(config) { return new Promise((resolve, reject) { const xhr new XMLHttpRequest(); // ...XHR实现代码 }); } }3.3 拦截器实战应用让我们看几个实际应用场景添加认证头const api new MiniAxios(); api.useRequestInterceptor(config { config.headers config.headers || {}; config.headers.Authorization Bearer ${localStorage.getItem(token)}; return config; });统一错误处理api.useResponseInterceptor( response response, error { if (error.response error.response.status 401) { // 跳转到登录页 window.location.href /login; } return Promise.reject(error); } );4. 取消请求实现取消请求是一个常被忽视但非常重要的功能。想象用户快速切换标签页时取消前一个未完成的请求可以显著提升性能。4.1 基于CancelToken的实现axios的取消机制基于CancelToken。我们可以实现类似的逻辑class CancelToken { constructor(executor) { let cancel; this.promise new Promise(resolve { cancel resolve; }); executor(message { cancel(message); }); } } class MiniAxios { // ...之前的代码 request(config) { if (config.cancelToken) { config.cancelToken.promise.then(message { // 这里应该取消XHR请求 if (this.xhr) { this.xhr.abort(); reject(new Cancel(message)); } }); } // ...其他代码 } }使用示例const source new CancelToken(c cancel c); api.get(/data, { cancelToken: source }).catch(err { if (err instanceof Cancel) { console.log(Request canceled, err.message); } }); // 取消请求 cancel(Operation canceled by user);4.2 实际应用场景避免重复请求let currentRequest null; function fetchData() { if (currentRequest) { currentRequest.cancel(New request started); } currentRequest new CancelToken(c cancel c); api.get(/data, { cancelToken: currentRequest }).then(response { currentRequest null; // 处理响应 }); }组件卸载时取消请求useEffect(() { const source new CancelToken(c cancel c); api.get(/data, { cancelToken: source }) .then(response { // 更新状态 }); return () { cancel(Component unmounted); }; }, []);5. 与json-server集成测试为了测试我们的库可以使用json-server快速搭建一个模拟API服务。5.1 设置json-server首先安装并创建测试数据npm install -g json-server创建db.json{ posts: [ { id: 1, title: json-server, author: typicode } ], comments: [ { id: 1, body: some comment, postId: 1 } ] }启动服务json-server --watch db.json5.2 测试我们的请求库const api new MiniAxios(); // 测试GET api.request({ url: http://localhost:3000/posts }).then(response { console.log(GET response:, response.data); }); // 测试POST api.request({ url: http://localhost:3000/posts, method: POST, data: { title: New Post, author: Me } }).then(response { console.log(POST response:, response.data); }); // 测试拦截器 api.useRequestInterceptor(config { console.log(Request sent to:, config.url); return config; }); api.useResponseInterceptor(response { console.log(Response received:, response.status); return response; });6. 性能优化与扩展6.1 请求缓存对于不常变化的数据可以添加缓存功能class MiniAxios { constructor() { this.cache new Map(); } request(config) { const cacheKey JSON.stringify(config); if (config.cache this.cache.has(cacheKey)) { return Promise.resolve(this.cache.get(cacheKey)); } return this.dispatchRequest(config).then(response { if (config.cache) { this.cache.set(cacheKey, response); } return response; }); } }6.2 并发控制限制同时进行的请求数量class MiniAxios { constructor(maxRequests 5) { this.maxRequests maxRequests; this.queue []; this.activeRequests 0; } request(config) { return new Promise((resolve, reject) { this.queue.push({ config, resolve, reject }); this.processQueue(); }); } processQueue() { if (this.activeRequests this.maxRequests this.queue.length) { const { config, resolve, reject } this.queue.shift(); this.activeRequests; this.dispatchRequest(config) .then(resolve) .catch(reject) .finally(() { this.activeRequests--; this.processQueue(); }); } } }6.3 超时处理添加请求超时功能dispatchRequest(config) { return new Promise((resolve, reject) { const xhr new XMLHttpRequest(); let isTimeout false; const timeoutTimer setTimeout(() { isTimeout true; xhr.abort(); reject(new Error(Timeout of ${config.timeout}ms exceeded)); }, config.timeout || 10000); xhr.onreadystatechange () { if (isTimeout) return; // ...正常处理 }; // ...其他XHR设置 }); }7. 完整实现与对比现在我们已经实现了axios的核心功能。让我们对比一下自建方案与axios功能自建方案axios基本请求拦截器取消请求自动转换JSON浏览器/Node仅浏览器都支持体积~3KB~10KB测试覆盖率需自测完善文档和社区支持无丰富在实际项目中如果需求简单且对体积敏感自建方案是个不错的选择。但对于复杂项目axios仍然是更稳妥的选择。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询