我在这里看到了一些奇怪的行为,这是出乎意料的,但是对于纯JavaScript而言,这使我感到直观。
我有一个表单控制器,它累积了一个this.thing
对象,该对象在最终提交时发送到服务器。这是一个多步骤表单,因此每个步骤都会向this.thing
添加一些数据。
因此控制器具有:
data() {
return {
thing: {},
};
},
此控制器的DOM标记有一个子对象,如:
<a-child
:initial-thing="thing"
></a-child>
孩子使用该道具显示其初始状态,因此它接收该道具并将其设置为自己的本地状态作为实例数据:
initialThing: {
type: Object,
required: true,
},
...
data() {
return {
thing: this.initialThing,
};
},
然后这个孩子有一个复选框,如下所示:
<a-checkbox
v-model="thing.field"
:initial-value="initialThing.field"
></a-checkbox>
一切正常,除了我刚刚注意到,当复选框更改时,它正在更改父控制器的thing.field
值。
我之所以提出这个问题,是因为我不了解Vue如何做到这一点,而对我来说唯一有意义的是,当孩子做thing: this.initialThing
时,它允许孩子打电话给二传手在this.initialThing
的那个字段上起作用。
如果我改为这样做,它将停止更改父母的状态:
data() {
return {
thing: { ...this.initialThing },
};
},
在我的实际应用中,它更复杂,因为有2个中间组件,所以孙子正在改变祖父母的状态,这源于我在这里描述的模式。
任何人都可以针对这里发生的事情提供一种教科书答案吗?我犹豫要依靠这种行为,因为驱动它的代码不是明确的。它使我的某些$emit()
事件变得多余,而倾向于使用这种间接/非显式的方式向上游发送数据。
还要清楚,这与v-model
无关,因为如果我这样做this.thing.field = 'new value';
也会这样做。我相信这与继承this.initialThing
上的getters / setters有关。依靠这种行为是否安全?如果我依靠它,它将使我的代码更加简洁,但是一个天真的人可能很难理解数据是如何将其变成祖父母组件的。
答案 0 :(得分:1)
这是一个浅副本,因此您不能阻止对子孙进行变异。
data() {
return {
thing: { ...this.initialThing },
};
},
解决方案如下:
data() {
return {
thing: JSON.parse(JSON.stringify(this.initialThing)),
};
},
const initialThing = {
age: 23,
name: {
first: "David",
last: "Collins",
}
}
const shallowCopy = { ...initialThing };
shallowCopy.age = 10;
shallowCopy.name.first = "Antonio"; // will mutate initialThing
console.log("init:", initialThing);
console.log("shallow:", shallowCopy);
const deepCopy = JSON.parse(JSON.stringify(initialThing));
deepCopy.age = 30;
shallowCopy.first = "Nicholas"; // will not mutate initialThing
console.log("------Deep Copy------");
console.log("init:", initialThing);
console.log("deep:", deepCopy);
工作方式:
JSON.stringify(this.initialThing)
这会将JSON Object
转换为String
类型。这意味着它将永远不会变异孩子。
然后JSON.parse
会将String
转换为Object
类型。
但是,使用stringify和parse在性能上会很昂贵。 :D
更新: 如果您使用lodash或可以添加外部库,则可以使用_.cloneDeep。
_.cloneDeep(value); // deep clone
_.clone(value); // shallow clone