Vue向子组件传值时修改子组件数据同步修改父数据如何解决
问题原因
出现联动修改的核心原因是JavaScript中对象、数组属于引用类型,布尔值、字符串、数字等属于值类型:
- 你代码里把布尔类型的prop
open赋值给data里的dialog时,是直接复制了布尔值本身,后续修改dialog不会影响父组件传入的prop值 - 但把对象类型的prop
addressData直接赋值给data里的address时,只是复制了对象的内存引用地址,两个变量指向内存里同一份对象数据,子组件修改address的属性时,本质就是在修改父组件里的原对象,自然会出现联动更新。
数组类型的countries同理,直接赋值也会出现双向联动问题。
解决方案
核心思路是在子组件初始化本地变量时,对传入的引用类型prop做拷贝,切断和父组件原数据的引用关联,根据数据结构复杂度可以选择不同的拷贝方式:
1. 浅拷贝(适用无嵌套结构的简单对象/数组)
如果addressData只有一层普通属性,没有嵌套的对象、数组,用浅拷贝即可:
data() { return { mdiClose, loading: false, valid: false, dialog: this.open, // 对象用展开运算符做浅拷贝 address: {...this.addressData}, // 数组用展开运算符做浅拷贝 countries: [...this.countries], cities: [], states: [], overlay: false, snackbar: { status: false, text: '', color: '#FF5252' }, rules: { required: value => !!value || 'Required.', }, } }
也可以用Object.assign({}, this.addressData)实现对象浅拷贝,效果和展开运算符一致。
2. 深拷贝(适用有多层嵌套结构的复杂对象/数组)
如果addressData里存在嵌套的对象、数组,浅拷贝只能拷贝第一层属性,嵌套层依然是引用关联,这时候需要做深拷贝:
- 无特殊对象(Date、RegExp、函数、循环引用)的普通数据,可以用JSON序列化方案实现,不需要额外引入工具:
address: JSON.parse(JSON.stringify(this.addressData))
- 数据结构复杂、存在特殊类型或者循环引用的场景,可以自己写递归逻辑实现深拷贝,或者直接引入工具库的深拷贝方法,比如lodash的
cloneDeep:
// 提前引入lodash的cloneDeep方法 address: _.cloneDeep(this.addressData)
额外注意事项
- Vue单向数据流规范要求子组件不要直接修改父组件传入的prop,哪怕是引用类型隐式修改的场景也要避免,否则会导致数据流混乱,大幅提升问题排查成本
- 如果子组件修改完地址数据后需要同步给父组件,正确的做法是通过
$emit抛出事件,把修改后的值作为参数传给父组件,由父组件自行更新自身数据,不要直接在子组件修改父级数据源。
内容的提问来源于stack exchange,提问作者Steve
相关产品推荐
相关产品推荐

