我修了一个注释代码,结果引出一连串线上 BUG…

概要

本文涉及:axios 拦截器、重复请求取消、请求 / 响应参数差异、axios 数据序列化、调用栈溢出排查

适合:遇到接口重复调用、拦截器异常、树形数据渲染崩溃的前端开发者

背景:附件上传异常,引出旧优化逻辑

某天,线上项目收到用户反馈:附件上传时,同时上传两个会出错。

排查后发现:旧系统框架使用 element-ui,新系统使用 ant-design-vue。两个框架的 upload 组件参数格式不一致,直接导致上传异常。

由于存量代码较多,完全重构上传组件成本较高,因此决定:利用项目中已有的 "重复请求优化" 能力,临时规避组件差异问题。

项目里原本就有一个优化逻辑:短时间内重复调用同一接口,只保留最后一次,前面的自动取消。

但同事反馈:这段代码看起来存在,但实际并未生效,重复接口并没有被合并。于是,这个问题转到了我这里。

理清来龙去脉,开始排查。

第一层问题:拦截器逻辑被注释失效

首先梳理重复请求优化的整体逻辑:通过 请求拦截器 + 响应拦截器 配合实现:

  • 把当前接口信息存入一个 pending 容器
  • 接口返回前,如果再次触发相同请求,取消前一次,只保留最后一次
  • 请求结束后,从容器中移除

在检查拦截器代码时发现:

请求拦截器中,removePending 一行被注释掉了,导致整个取消逻辑不完整。

(上图:请求拦截器代码,核心移除逻辑被注释)

查看历史记录,这是项目初期就被注释的代码,前序团队并未启用。为了解决附件上传问题,我先将这行代码恢复启用。

通知测试验证,附件上传功能恢复正常,问题暂时修复。

但真正的问题,才刚刚开始浮现。

第二层问题:树结构页面数据展示异常,调用栈溢出

复测通过后不久,测试反馈:某树表格页面数据无法展示。

控制台提示:调用栈溢出(Maximum call stack size exceeded)

(上图:树表格页面接口报错,调用栈溢出)

这个功能长期稳定运行,理论上不应该突然出问题。

同事提议:把刚才修改的拦截器代码恢复注释,试试看。

结果:注释掉 removePending 后,页面立刻恢复正常。

问题很明确:启用重复请求拦截逻辑 → 触发新 BUG:树数据展示失败、栈溢出。

先贴出核心工具函数 removePending:作用是遍历 pending 容器,匹配相同请求并执行取消。

(上图:removePending 核心逻辑)

根因一:请求 / 响应拦截器参数结构不一致

排查到这里,大部分同学应该已经能看出问题:

请求拦截器 与 响应拦截器 都将自身默认的参数传给了 removePending 函数,但,两个拦截器的默认参数的格式,是不同的!

问题,就出在这里

(上图:请求拦截器参数结构:config)

  1. 请求拦截器参数

    (上图:响应拦截器参数结构:response,需通过 .config 获取请求配置

  2. 响应拦截器参数 确认:响应拦截器里,应该传 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。

整个问题链路可以梳理为:

  1. 历史代码:重复请求拦截逻辑被注释,处于半失效状态
  2. 业务场景:新旧框架 upload 组件参数不一致,需要依赖拦截器规避
  3. 第一层 BUG:拦截器参数结构不匹配,直接传 response 导致大数据序列化爆栈
  4. 第二层 BUG:axios 自动序列化 data,导致请求 / 响应阶段 config.data 类型不一致,匹配失败
  5. 最终表现:树结构页面崩溃、pending 容器累积、隐蔽且难定位

核心结论

  • 请求 / 响应拦截器参数结构不同,不能直接混用
  • 避免对大量级、深度嵌套数据无脑执行 qs.stringify
  • axios 会自动序列化 data,请求 / 响应阶段类型可能不一致
  • 控制台打印对象 ≠ 代码运行时真实值,务必以类型 / 快照为准

这类问题隐蔽性强、和底层框架强相关,也是前端工程化中非常典型的 "隐形坑",记录下来供大家参考避坑。

相关推荐
mCell10 小时前
如何零成本搭建个人站点
前端·程序员·github
mCell11 小时前
为什么 Memo Code 先做 CLI:以及终端输入框到底有多难搞
前端·设计模式·agent
恋猫de小郭11 小时前
AI 在提高你工作效率的同时,也一直在增加你的疲惫和焦虑
前端·人工智能·ai编程
少云清12 小时前
【安全测试】2_客户端脚本安全测试 _XSS和CSRF
前端·xss·csrf
银烛木12 小时前
黑马程序员前端h5+css3
前端·css·css3
m0_6070766012 小时前
CSS3 转换,快手前端面试经验,隔壁都馋哭了
前端·面试·css3
听海边涛声12 小时前
CSS3 图片模糊处理
前端·css·css3
IT、木易12 小时前
css3 backdrop-filter 在移动端 Safari 上导致渲染性能急剧下降的优化方案有哪些?
前端·css3·safari
0思必得012 小时前
[Web自动化] Selenium无头模式
前端·爬虫·selenium·自动化·web自动化
anOnion12 小时前
构建无障碍组件之Dialog Pattern
前端·html·交互设计