fetch拦截器需通过JavaScript劫持全局fetch函数实现,本质是用Proxy重写window.fetch,在请求前/响应后插入逻辑;HTML本身不提供拦截能力,且必须在早期执行、避免重复代理,并注意Request实例不可变、body需clone才能多次读取。

HTML 里没法直接做 fetch 拦截器
浏览器环境下的 fetch 是原生 API,没有内置拦截机制,HTML 本身更不提供任何运行时钩子。所谓“HTML 做 fetch 拦截器”是个常见误解——真正能动手的地方是 JavaScript 运行时,而且必须在 fetch 调用前完成代理替换。
你看到的“拦截器”效果,本质是重写全局 fetch 函数,或用 Proxy 包裹它,所有后续调用都会经过你的逻辑。但这不是 HTML 的能力,而是 JS 对运行时行为的劫持。
用 Proxy 重写 window.fetch 最简单可靠
这是目前最轻量、兼容性好(Chrome 49+ / Firefox 53+ / Safari 10.1+)、不影响原有调用方式的做法。核心思路:保存原始 fetch,再用 Proxy 包一层,在发起请求前/收到响应后插入自定义逻辑。
- 必须在页面最早期执行(比如
<script></script>放在里),否则已有模块可能已缓存旧的fetch - 注意不要重复代理:检查
window.__isFetchProxied这类标记,避免多次覆盖 -
response.clone()才能多次读取 body,直接response.json()会消耗流,后续再读就报错TypeError: Failed to execute 'json' on 'Response': body stream already read - 示例代码:
const originalFetch = window.fetch;
window.fetch = new Proxy(originalFetch, {
apply(target, thisArg, args) {
const [input, init] = args;
console.log('【请求发出】', input, init?.method || 'GET');
return target.apply(thisArg, args)
.then(response => {
console.log('【响应收到】', response.status, response.url);
// 注意:这里不能直接 await response.json(),除非你 clone
return response;
})
.catch(err => {
console.error('【请求失败】', err);
throw err;
});
}
});
拦截器里怎么统一加 token 或改 header
修改请求头必须在 init 存在且为对象的前提下操作;如果传的是 Request 实例,得用 new Request(url, { ...originalRequest, headers }) 重建,因为 Request.headers 是只读的。
立即学习“前端免费学习笔记(深入)”;
- 判断参数类型:
input instanceof Request为 true 说明传入的是Request对象 - 安全加 header:用
new Headers(init?.headers)克隆后再.set('Authorization', 'Bearer xxx') - 不要直接修改
init.headers,它可能是undefined或Headers实例(不可赋值) - 示例片段(加 token):
apply(target, thisArg, args) {
let [input, init] = args;
if (!init || typeof init !== 'object') init = {};
const headers = new Headers(init.headers);
headers.set('Authorization', 'Bearer ' + localStorage.getItem('token') || '');
if (input instanceof Request) {
input = new Request(input, { ...init, headers });
} else {
init.headers = headers;
}
return target.apply(thisArg, [input, init]);
}
为什么 axios 拦截器不适用于纯 fetch 场景
axios 的 interceptors.request.use 和 interceptors.response.use 只对它自己封装的请求生效,和原生 fetch 完全无关。如果你项目里混用两者,必须分别处理:axios 用它的拦截器,fetch 用上面说的 Proxy 方案。
更大的坑在于第三方库:像 graphql-request、swr、react-query 默认都用 fetch,它们发的请求会被你的 Proxy 拦到——这本来是好事,但如果你在拦截器里抛了未捕获异常,整个请求链就断了,且错误堆栈不直观。
- 务必在拦截器的
then/catch中兜底,避免意外中断 - 避免在拦截器里做异步等待(如
await getToken()),会阻塞所有fetch,UI 卡死 - 调试时加个开关变量,上线前关掉 console 日志,否则大量请求刷屏
真正麻烦的从来不是“怎么加”,而是“加在哪”“加完谁还在用旧的”“加了之后别人家的库有没有悄悄绕过”。别信“一行代码搞定”,先看清楚你项目里到底有几个地方在发 fetch。



















