JavaScript如何正确更新嵌套对象属性(解决Next.js只读报错)
多层嵌套对象部分属性更新最佳实践
问题根因
两类报错和异常的本质原因:
- 直接赋值报只读错误:Next.js 环境中的配置对象、状态对象默认经过
Object.freeze冻结处理,属于只读值,直接修改原对象属性会触发类型错误。 - 单层对象展开丢失属性:ES6 对象展开运算符是浅拷贝逻辑,仅会复制对象第一层属性,直接替换嵌套层级的对象会覆盖该路径下所有未手动声明的原有属性。
可选实现方案
1. 原生多层嵌套展开(无依赖首选)
不需要引入任何第三方库,对每一层需要更新的嵌套结构做展开操作,最终生成全新的对象,不会修改原对象,也不会丢失未更新的属性:
const newConfig = { ...adjustedConfig, width: 1800, disableAnimations: true, stars: { ...adjustedConfig.stars, size: 20, exponent: -0.26, limit: 10 }, constellations: { ...adjustedConfig.constellations, lineStyle: { ...adjustedConfig.constellations.lineStyle, width: 2 } }, background: { ...adjustedConfig.background, width: 4 } };
该方案优势是语法完全原生,行为可控无黑盒,适合嵌套层级在3层以内、不想引入额外依赖的场景。
2. 原生深拷贝后修改(简单场景首选)
现代JS运行环境原生支持structuredClone方法,可以对普通配置对象做完整深拷贝,拷贝生成的新对象可自由修改,不会影响原只读对象:
const newConfig = structuredClone(adjustedConfig); newConfig.stars.size = 20; newConfig.width = 1800; newConfig.disableAnimations = true; newConfig.stars.exponent = -0.26; newConfig.stars.limit = 10; newConfig.constellations.lineStyle.width = 2; newConfig.background.width = 4;
注意:structuredClone无法拷贝函数、Symbol类型值,存在循环引用的对象也无法正常克隆,纯数据类配置对象使用完全没有兼容问题,Next.js 所有支持的现代运行环境都已实现该API。
3. 工具库简化写法(频繁操作嵌套对象首选)
如果项目本身已经引入了工具库,或者需要频繁做嵌套对象的不可变更新,可以选择对应工具简化代码:
- 若已引入lodash,可使用
cloneDeep做深拷贝后直接赋值修改,逻辑和structuredClone一致,兼容性更好:
import cloneDeep from 'lodash/cloneDeep'; const newConfig = cloneDeep(adjustedConfig); // 后续直接按路径赋值即可 newConfig.stars.size = 20;
- 若做状态更新场景,推荐使用immer库,不需要全量深拷贝,直接写赋值语法即可自动生成符合不可变要求的新对象,性能更优:
import { produce } from 'immer'; const newConfig = produce(adjustedConfig, (draft) => { draft.stars.size = 20; draft.width = 1800; draft.disableAnimations = true; draft.stars.exponent = -0.26; draft.stars.limit = 10; draft.constellations.lineStyle.width = 2; draft.background.width = 4; });
所有场景下都不要尝试修改原只读对象,Next.js配置、React状态、状态管理库的存储值都要求不可变更新,始终返回新的对象引用,避免出现配置不生效、组件渲染异常等难以排查的问题。
内容的提问来源于stack exchange,提问作者Roman
相关产品推荐
相关产品推荐

