Vue组件对实例数据的支持集使上游值突变

时间:2019-09-08 07:09:42

标签: javascript vue.js vuejs2

我在这里看到了一些奇怪的行为,这是出乎意料的,但是对于纯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有关。依靠这种行为是否安全?如果我依靠它,它将使我的代码更加简洁,但是一个天真的人可能很难理解数据是如何将其变成祖父母组件的。

1 个答案:

答案 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