Vue 组件对实例数据的 prop 设置改变了上游值



我在这里看到了一些出乎意料的奇怪行为,但就纯JavaScript而言,这对我来说是直观的。

我有一个表单控制器,它累积了一个this.thing对象,该对象在最终提交时发送到服务器。这是一个多步骤的形式,所以每个步骤都会向this.thing添加一些数据。

因此,控制器具有:

data() {
return {
thing: {},
};
},

此控制器的 DOM 标记有一个子项,如下所示:

<a-child
:initial-thing="thing"
></a-child>

子项使用该 prop 显示其初始状态,因此它接收 prop 并将其设置为自己的本地状态作为实例数据:

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上调用该字段上的 setter 函数。

如果我这样做,它会停止改变父级的状态:

data() {
return {
thing: { ...this.initialThing },
};
},

在我的实际应用程序中,它更复杂,因为有 2 个中间组件,所以孙子正在改变祖父母的状态,它源于我在这里描述的模式。

谁能为这里发生的事情提供一种教科书式的答案?我对依赖这种行为犹豫不决,因为驱动它的代码不是显式的。它使我的一些$emit()事件变得多余,有利于使用这种间接/非显式的方式向上游发送数据。

另外需要明确的是,这与v-model无关,因为如果我这样做,它也会这样做this.thing.field = 'new value';.我相信这与继承this.initialThing上的getter/setter有关。依赖这种行为是否安全?如果我依赖它,它将使我的代码更加简洁,但是一个天真的人可能很难理解数据是如何进入祖父级组件的。

这是一个浅拷贝,所以你不能阻止孙子孙女的变异。

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)

这会将 JSONObject转换为String类型。这意味着它永远不会再让孩子变异了。 然后JSON.parse会将String转换为Object类型。

但是,使用 stringify 和解析在性能上会很昂贵。 :D

更新:如果您使用的是 lodash 或者可以添加外部库,则可以使用 _.cloneDeep。

_.cloneDeep(value); // deep clone
_.clone(value); // shallow clone

最新更新