概要
本文涉及:axios 拦截器、重复请求取消、请求 / 响应参数差异、axios 数据序列化、调用栈溢出排查
适合:遇到接口重复调用、拦截器异常、树形数据渲染崩溃的前端开发者
背景:附件上传异常,引出旧优化逻辑
某天,线上项目收到用户反馈:附件上传时,同时上传两个会出错。
排查后发现:旧系统框架使用 element-ui,新系统使用 ant-design-vue。两个框架的 upload 组件参数格式不一致,直接导致上传异常。
由于存量代码较多,完全重构上传组件成本较高,因此决定:利用项目中已有的 "重复请求优化" 能力,临时规避组件差异问题。
项目里原本就有一个优化逻辑:短时间内重复调用同一接口,只保留最后一次,前面的自动取消。
但同事反馈:这段代码看起来存在,但实际并未生效,重复接口并没有被合并。于是,这个问题转到了我这里。
理清来龙去脉,开始排查。
第一层问题:拦截器逻辑被注释失效
首先梳理重复请求优化的整体逻辑:通过 请求拦截器 + 响应拦截器 配合实现:
- 把当前接口信息存入一个 pending 容器
- 接口返回前,如果再次触发相同请求,取消前一次,只保留最后一次
- 请求结束后,从容器中移除
在检查拦截器代码时发现:
请求拦截器中,removePending 一行被注释掉了,导致整个取消逻辑不完整。
(上图:请求拦截器代码,核心移除逻辑被注释) 

查看历史记录,这是项目初期就被注释的代码,前序团队并未启用。为了解决附件上传问题,我先将这行代码恢复启用。
通知测试验证,附件上传功能恢复正常,问题暂时修复。
但真正的问题,才刚刚开始浮现。
第二层问题:树结构页面数据展示异常,调用栈溢出
复测通过后不久,测试反馈:某树表格页面数据无法展示。
控制台提示:调用栈溢出(Maximum call stack size exceeded) 。
(上图:树表格页面接口报错,调用栈溢出) 
这个功能长期稳定运行,理论上不应该突然出问题。
同事提议:把刚才修改的拦截器代码恢复注释,试试看。
结果:注释掉 removePending 后,页面立刻恢复正常。
问题很明确:启用重复请求拦截逻辑 → 触发新 BUG:树数据展示失败、栈溢出。
先贴出核心工具函数 removePending:作用是遍历 pending 容器,匹配相同请求并执行取消。
(上图:removePending 核心逻辑) 
根因一:请求 / 响应拦截器参数结构不一致
排查到这里,大部分同学应该已经能看出问题:
请求拦截器 与 响应拦截器 都将自身默认的参数传给了 removePending 函数,但,两个拦截器的默认参数的格式,是不同的!
问题,就出在这里
(上图:请求拦截器参数结构:config)
-
请求拦截器参数

(上图:响应拦截器参数结构:response,需通过 .config 获取请求配置
-
响应拦截器参数
确认:响应拦截器里,应该传 response.config,而不是直接传 response。
直接传 response 会发生什么?response 中包含完整的接口返回 data,对某些大体积数据(比如深度树结构)执行 qs.stringify,会因为递归深度过高,直接爆调用栈。
这就是树页面崩溃、栈溢出的直接原因。
修复方式:响应拦截器调用 removePending 时,统一使用 .config。
修改后,栈溢出问题消失,页面恢复正常。
第三层问题:pending 容器只增不减,内存累积
功能虽然恢复,但我在复查时发现一个隐藏问题:
接口执行完毕后,pending 容器并没有完全清空,部分请求一直残留。
上图:pending 容器不断累积,未正常清空 
逻辑上:
- 请求拦截器:add → 加入容器
- 响应拦截器:remove → 移出容器流程是闭环的,不应该残留。
继续排查,最终定位到:config.data。
分别打印对比:
-
请求拦截器中的
config.data
-
响应拦截器中的
config.data
现象:
- 请求拦截器中:
config.data是 对象 - 响应拦截器中:
config.data变成了 JSON 字符串
但控制台显示却是相同的,两者的 data 均为下图所示。

这里有一个前端非常经典的 "隐形坑":控制台打印对象是懒加载引用,看到的不一定是代码执行时的真实值。
根因二:axios 自动序列化 data
回顾一下 removePending 函数,问题就出在 config.data 上 
真正原因来自 axios 底层行为:
axios 会在 请求拦截器执行完毕后、发送请求前 ,自动把
config.data从 JS 对象序列化为 JSON 字符串。
这就导致:
- 请求拦截器操作的是:原始对象
- 响应拦截器拿到的是:序列化后的字符串
两者在 removePending 里做匹配、拼接、序列化时,自然无法正确匹配,最终表现为:部分请求能移除、部分请求移除失败,容器只增不减。
最终修复方案:在生成请求唯一标识时,统一处理 data 格式,对 JOSN 格式进行解析,保证请求 / 响应阶段类型一致。
修复后,pending 容器能够正常添加、正常移除,逻辑完全闭环。
修复后的代码: 
最终,这个优化代码的逻辑与实现,完全正常了!
总结与复盘
这是一个典型的 架构阶段遗留、长期被掩盖、因业务 / 框架变更才暴露 的复合型 BUG。
整个问题链路可以梳理为:
- 历史代码:重复请求拦截逻辑被注释,处于半失效状态
- 业务场景:新旧框架 upload 组件参数不一致,需要依赖拦截器规避
- 第一层 BUG:拦截器参数结构不匹配,直接传 response 导致大数据序列化爆栈
- 第二层 BUG:axios 自动序列化 data,导致请求 / 响应阶段 config.data 类型不一致,匹配失败
- 最终表现:树结构页面崩溃、pending 容器累积、隐蔽且难定位
核心结论
- 请求 / 响应拦截器参数结构不同,不能直接混用
- 避免对大量级、深度嵌套数据无脑执行
qs.stringify - axios 会自动序列化
data,请求 / 响应阶段类型可能不一致 - 控制台打印对象 ≠ 代码运行时真实值,务必以类型 / 快照为准
这类问题隐蔽性强、和底层框架强相关,也是前端工程化中非常典型的 "隐形坑",记录下来供大家参考避坑。