
一、为什么要 Hook Fetch在前端开发中fetch已经取代XMLHttpRequest成为现代浏览器发起网络请求的标准方式。当我们需要对全站请求做统一处理时 —— 比如接口埋点、请求签名、Token 注入、响应统一拦截、错误重试、Mock 数据等 —— 最优雅的方案就是在底层对fetch进行全局 Hook。相比于在业务代码中层层封装请求函数全局 Hook Fetch的优势在于无侵入业务代码无需修改所有调用fetch的地方自动生效全覆盖能拦截第三方库、组件库内部发出的 fetch 请求一次性在入口文件执行一次全生命周期生效这也是前端监控 SDK、埋点系统、API 网关客户端最常用的技术手段。二、Hook 的核心原理window.fetch本质上是挂载在全局对象上的一个原生函数。Hook 的核心思路非常经典保存原始的fetch函数引用重写window.fetch在其中插入自定义逻辑内部调用原始fetch保证原有功能不受影响对请求和响应进行代理处理简单来说就是函数代理 闭包保存原方法和 AOP面向切面编程思想一致。三、最简实现第一个 Fetch Hook先从最基础的版本入手实现请求日志打印// 保存原始 fetch const originalFetch window.fetch; // 重写 fetch window.fetch function (...args) { // 【请求拦截层】 const [url, options {}] args; console.log([Fetch Request] ${options.method || GET} ${url}); console.log(Request body:, options.body); // 调用原始 fetch返回 Promise return originalFetch.apply(this, args).then((response) { // 【响应拦截层】 console.log([Fetch Response] ${url} status: ${response.status}); return response; }).catch((error) { // 【错误拦截层】 console.error([Fetch Error] ${url}, error); throw error; }); };这就是一个完整可用的最小 Hook 实现。所有fetch()调用都会经过我们的拦截逻辑同时行为与原生完全一致。四、进阶实战完整的拦截器体系4.1 读取响应体的坑点原生Response对象的 body 是可读流ReadableStream只能读取一次。如果我们在拦截器里调用了response.json()或response.text()业务代码再读就会报错。解决方案是使用clone()克隆响应return originalFetch.apply(this, args).then(async (response) { // 克隆响应避免消耗原始流 const clonedRes response.clone(); const data await clonedRes.json().catch(() null); console.log(Response data:, data); // 返回原始响应给业务代码 return response; });4.2 统一注入请求头最常见的需求给所有请求自动加上 Token、时间戳、签名等 Header。window.fetch function (url, options {}) { // 合并自定义 Header const customHeaders { Authorization: Bearer ${localStorage.getItem(token) || }, X-Request-Time: Date.now().toString(), }; options.headers { ...options.headers, ...customHeaders, }; return originalFetch.call(this, url, options); };注意options.headers可能是Headers对象实例也可能是普通对象生产环境建议做类型兼容处理。4.3 统一错误处理与状态码拦截对 401、403、500 等状态码做全局处理避免每个请求都写重复的判断逻辑。return originalFetch.apply(this, args).then((response) { switch (response.status) { case 401: // 未授权跳转登录页 window.location.href /login; break; case 403: console.error(无权限访问该接口); break; case 500: console.error(服务器内部错误); break; } return response; });4.4 请求失败自动重试针对网络波动或 5xx 错误实现自动重试机制window.fetch function (...args) { const maxRetries 3; let retryCount 0; const request () { return originalFetch.apply(this, args).then((response) { if (response.status 500 retryCount maxRetries) { retryCount; console.log(重试第 ${retryCount} 次); return request(); } return response; }).catch((error) { if (retryCount maxRetries) { retryCount; return request(); } throw error; }); }; return request(); };五、高级场景修改请求与响应5.1 修改请求 URL统一加前缀window.fetch function (url, options) { // 相对路径自动拼接接口前缀 if (!url.startsWith(http)) { url /api url; } return originalFetch.call(this, url, options); };5.2 修改响应数据Mock / 数据转换通过new Response()构造新的响应对象完全替换返回结果return originalFetch.apply(this, args).then(async (response) { // 对特定接口做数据 Mock if (url.includes(/api/mock-data)) { const mockData { code: 0, data: { list: [] } }; return new Response(JSON.stringify(mockData), { status: 200, headers: response.headers, }); } return response; });5.3 取消请求与超时控制结合AbortController实现全局超时window.fetch function (url, options {}) { const timeout 10000; // 10秒超时 const controller new AbortController(); const signal controller.signal; const timeoutId setTimeout(() controller.abort(), timeout); return originalFetch.call(this, url, { ...options, signal: options.signal || signal, }).finally(() { clearTimeout(timeoutId); }); };六、常见坑点与最佳实践Response 流只能读取一次读取 body 前务必clone()否则业务侧会拿到空响应。Headers 类型兼容options.headers可能是Headers、对象、数组三种形式合并时要做判断。保留 this 指向使用originalFetch.apply(this, args)而非直接originalFetch(...args)确保上下文正确。避免循环 Hook如果代码多次执行 Hook 逻辑会造成函数嵌套导致性能问题和逻辑异常建议加守卫标记。异常透传catch 中处理完错误后记得throw error不要吞掉异常否则业务代码无法捕获。与 polyfill 兼容如果项目引入了whatwg-fetch等 polyfill确保 Hook 在 polyfill 加载之后执行。七、守卫标记防止重复 Hookif (window.__fetchHooked__) { console.warn(Fetch 已被 Hook跳过重复执行); } else { window.__fetchHooked__ true; // 执行 Hook 逻辑... }八、总结Hook Fetch API 是前端底层能力扩展的经典实战核心思想就是函数代理 切面注入。从最简单的日志打印到完整的请求签名、响应拦截、错误重试、超时控制、数据 Mock都可以基于这套模式扩展。在实际项目中通常会将这套逻辑封装成一个独立的请求拦截器模块在应用入口初始化一次就能为全站请求提供统一的治理能力。这也是构建前端监控 SDK、API 网关客户端、埋点系统的基础能力之一。这是一篇偏向实战的技术文章如果你需要补充更多场景比如结合 TypeScript 类型、和 Axios 拦截器对比、或在 React/Vue 项目中的接入方式可以随时告诉我。