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