Axios 全局 json-bigint 解析导致空原型对象渲染崩溃

Issue 编号:76
创建:2026-07-24T10:23:07Z
更新:2026-10-27T04:48:58Z
状态:open
标签:["blog:publish", "javascript", "笔记", "前端", "react", "bugfix", "axios", "json-bigint", "AI 写作为主"]
原始发布日期:2026-06-01T00:00:00.000Z

正文

问题现象

在 React 表单详情页中,页面加载时突然崩溃:
错误最终落在 Ant Design 的 <Input /> 渲染链路中。业务代码中存在一个隐藏表单项,用来保存联动配置对象:
nextConfig 并不是字符串,而是一整个对象。此前页面可以正常运行,为什么突然开始报错?

背景: 为了解决大整数 ID 精度丢失

后端部分业务 ID 可能超过 JavaScript 的安全整数范围:
例如:
如果使用浏览器原生 JSON.parse,这个 ID 会在解析时丢失精度。事后复盘,其实最合理的做法是让后端直接将这类 ID 序列化为字符串,前端就完全不需要特殊处理了。但当时后端短期内无法调整接口,只能前端兜底,于是我在 Axios 实例中全局启用了 json-bigint:
这个改动看起来很合理:
  • 超出精度范围的整数会保存为字符串
  • 普通数字仍然保持数字类型
  • 所有接口自动获得大整数兼容能力
但是正是这种全部接口的出参都替换导致了 bug

根因:json-bigint 返回空原型对象

这两种对象很像,但并不等价。
这不是推测,而是 json-bigint 在解析 JSON 对象的时候,sidorares/json-bigint 的 lib/parse.js 中, object() 在解析每个 { ... } 时显式写入:
所以经过 json-bigint 的对象,是通过 Object.create(null) 创建的。
然后普通对象继承了 Object.prototype:
空原型对象没有继承 toString、valueOf 等方法:
因此,全局替换 Axios 响应解析器后,不只是超大整数发生了变化,所有接口返回的普通 JSON 对象也悄悄变成了空原型对象。
当 Ant Design 的 <Input /> 尝试将隐藏表单项中的 nextConfig 对象转换成字符串时,由于原型没有 toString() 就崩溃了。
然后由于 nextConfig 对应的 value 是一个对象,通过受控组件的写法注入了 input。此时 <Input /> 收到的 value 就是 nextConfig 这个对象 , 且是由 json-bigint 解析出来的空原型对象。

<Input /> 内部发生了什么?

Ant Design 的 Input 底层是对原生 <input> 元素的封装。当它处理 value 时,内部大致会经过这样的路径:
  1. 将 value 写入 DOM: React 将 value 赋给原生 <input> 元素的 .value 属性。DOM API 要求 .value 必须是字符串,因此浏览器会隐式调用 String(value) 进行转换。
  1. String() 的类型转换规则: 当 String() 遇到一个对象时,JavaScript 引擎按 ToPrimitive 尝试:
      • 先调用 obj[Symbol.toPrimitive]("string")(如果有的话)
      • 再调用 obj.toString() —— 期望返回一个 primitive
      • 再调用 obj.valueOf() —— 期望返回一个 primitive
      • 如果以上都失败或返回的不是 primitive,抛出 TypeError(见 String)
  1. 空原型对象的致命问题: 通过 Object.create(null) 创建的对象,原型链是空的。它没有继承 Object.prototype 上的 toString() 和 valueOf() 方法:
    1. 崩溃点: 浏览器调用 String(nextConfig) → 找不到 toString() → 找不到 valueOf() → 抛出 TypeError: Cannot convert object to primitive value。

    为什么此前页面可以正常运行?

    虽然把对象塞进 <Input /> 本身就不是规范的做法,但之前因为普通对象能隐式转换为字符串,所以 " 巧合地 " 没出问题。引入 json-bigint 后,所有接口返回的嵌套对象都变成了空原型对象,这个隐藏的隐患才被触发。
    在引入 json-bigint 之前,Axios 使用浏览器原生的 JSON.parse 解析响应。原生 JSON.parse 返回的是普通对象(继承自 Object.prototype),因此:

    修复思路

    最好的方法:后端 ID 字段返回 string

    推动后端将超出 JS 安全整数范围的 ID 直接序列化为字符串:
    收益:
    • 前端不需要 json-bigint,也没有空原型对象的副作用
    • 字段语义明确,Web / 移动端 / 小程序一致,不依赖路由表或启发式
    • 后端通常只需调整序列化配置,改动成本低
    这是唯一真正「字段层面」且没有精度竞态的方案。下文过渡方案仅在后端尚未配合时使用。

    过渡方案: 按响应内容启用 json-bigint

    需要去判断 原始 JSON 字符串里是否可能出现超大整数。
    解析工具
    reviver 中的 { ...value } 必不可少: 只要走了 json-bigint,嵌套 object 就是 null 原型,必须还原,否则仍可能触发隐式 String() 崩溃。

    总结

    这次问题的核心是: 引入中间件或者做出会影响全局的变更的时候需要谨慎。
    URL 白名单、字段层面特殊处理以及全局 json-bigint 都只是后端未改前的临时手段,不应该长期使用。
    <!-- issues-blog:source=d1:post.axios-global-json-bigint-null-prototype-object-render-crash --> <!-- issues-blog:published-at=2026-06-01T00:00:00.000Z -->
    原始 Markdown(保真归档)
    上一篇
    最近的变化-锻炼身体、调整学习状态
    下一篇
    bug-form 回填错误