Vuejs2 使用组件(尤其是嵌套组件)时,会混淆特性、数据和计算值

Vuejs2 使用组件(尤其是嵌套组件)时,会混淆特性、数据和计算值,vuejs2,vue-component,vuex,Vuejs2,Vue Component,Vuex,我真的很困惑如何从父级传递属性,并在子级中更新它们,然后将这些更改全部发送回父级。通过引用,它看起来是可行的,但从我读到的每一件事来看,这不是正确的方法 我有一个父组件,用于“切换”创建表单。创建工件的新实例并将其传递到“create”组件中,该组件在其他地方使用。创建组件有一些公共属性,但是可以通过插槽在父级添加自定义属性 在ArtifactCreate组件中,它将该道具的“克隆”传递给表单和自定义属性 我试图理解如何获取“prop”值,在内部使用它,然后将最终结果冒泡到ArtifactCre

我真的很困惑如何从父级传递属性,并在子级中更新它们,然后将这些更改全部发送回父级。通过引用,它看起来是可行的,但从我读到的每一件事来看,这不是正确的方法

我有一个父组件,用于“切换”创建表单。创建工件的新实例并将其传递到“create”组件中,该组件在其他地方使用。创建组件有一些公共属性,但是可以通过插槽在父级添加自定义属性

在ArtifactCreate组件中,它将该道具的“克隆”传递给表单和自定义属性

我试图理解如何获取“prop”值,在内部使用它,然后将最终结果冒泡到ArtifactCreate组件,然后再冒泡到父级

“子”组件(ArtifactCreateForm和DatasetProperties)没有任何其他方法,直接更新传入的prop引用。DatasetProperties也可以在其他地方使用

在大多数情况下,我有一个“父级”,我想处理主交互,但需要将一些模型向下传递给一个或多个组件,而这些组件又可能在其子级中传递该模型

结构与此类似:

Parent Component (somewhat like the controller handling main actions)
   |
   |--- View Component (takes in 'artifact' prop and passes it down)
            |
            |--- Child Component
                     |
                     |--- GrandChild 1..x
   |-----------------|--- GrandChild (through slot)
我对属性与数据和/或计算值之间的关系以及如何正确处理孙辈中的数据感到困惑。我对向下传递数据和向上发射事件的概念很熟悉,我似乎掌握了如何使用基本属性来实现这一点,但如何使用具有许多属性的对象来实现这一点

我的问题是:

  • 传递道具并在组件内部处理它们(作为副本),然后将其备份到父级的正确方法是什么

  • 当从较低的组件冒泡事件是重复它直到它到达父级的唯一方法时

  • 我使用“对象”作为道具/数据,而不是单个值。引用被更新(这是一个副本),然后冒泡到父级(如我所愿)。这条路对吗

  • (仅供参考,从Java到Vue,这对我来说是一个全新的世界)

    父组件

    这是我进入创建组件的条目。它管理实例(在本例中是一个新实例),并将其传递给ArtifactCreate组件,后者在内部克隆它。更新后的副本以气泡形式返回到此处保存

    
    
    支持:

    计算:{
    新工件:{
    get():数据集{
    返回newDataset();
    }
    }
    },
    方法:{
    保存(项目:数据集){
    此.$store.dispatch(“saveDataset”,项);
    },
    
    工件创建组件(子/容器组件)

    
    基本的
    先进的
    
    并且它得到以下支持:

    导出默认Vue.extend({
    名称:“工件创建”,
    道具:{
    人工制品:{
    类型:对象作为道具,
    默认值:{}
    },
    },
    计算:{
    内部:{
    get():IArtifact{
    返回克隆(this.artifact);
    },
    }
    },
    方法:{
    保存(项目){
    此.emit('save',此.internal);
    可见=假;
    },
    },
    });
    
    ArtifactCreateForm组件(ArtifactCreate中的子组件)

    
    
    支持:

    导出默认Vue.extend({
    名称:“ArtifactCreateForm”,
    道具:{
    人工制品:{
    类型:对象作为道具,
    默认值:{}
    },
    },
    });
    
    DatasetProperties组件(工件中的子项通过插槽在父项中创建/注册)

    
    
    支持:

    导出默认Vue.extend({
    名称:“数据集属性”,
    道具:{
    人工制品:{
    类型:对象作为道具,
    默认值:{}
    }
    },
    })
    
    我已经准备好了Vuex,并且“可以”使用它,但创建新对象似乎有点过火了?但类似于如何处理仍然试图在适当的位置/时间获取数据的数据

    传递道具并在组件内部处理它们(作为副本),然后将其备份到父级的正确方法是什么

    作为副本,就像您在
    ArtifactCreate
    中所做的那样:克隆道具,根据需要对其进行变异,并在需要时将其发送给父级以及事件

    但是,如果您的子组件不需要有自己的内部值,则不应克隆道具,而应仅发射事件并让父组件处理突变。子组件中的情况如下:

    <v-text-field 
      :value="artifact.source"
      label="Source"
      @input="$emit('updateSource', $event)"
    >
    
    
    
    在您的示例中,奇怪的是您的子组件克隆了父组件刚刚创建的数据。难道子组件就不能负责调用
    newDataset()

    当从较低的组件冒泡事件是重复它直到它到达父级的唯一方法时

    是的,唯一正确的方法(见下一个答案)

    我使用“对象”作为道具/数据,而不是单个值。引用被更新(这是一个副本),然后冒泡到父对象(如我所愿)。这是正确的方法吗

    您在
    ArtifactCreateForm
    DatasetProperties
    中所做的操作有效,但被认为是一种反模式。原因是数据存在于父组件中,并且由子组件进行变异,而父组件未知变异源。如果您的组件hiera安检变得复杂。正确的方法是发送事件

    这就是为什么在我看来,您应该小心地拆分组件:您真的有
    ArtifactCreateForm
    的重用潜力吗
    <template>
      <v-form>
        <v-text-field
          :value="value.name"
          @input="update(m => (m.name = $event))
          label="Name*" 
          required>
        </v-text-field>
        <v-text-field
          :value="value.description"
          @input="update(m => (m.description = $event))
          label="Description" 
          required>
        </v-text-field>
      </v-form>
    </template>
    
    
    export default Vue.extend({
      name: 'ArtifactCreateForm',
      props:{
        value: {
          type: Object as Prop<IArtifact>
        }
      },
      methods: {
        update(cb: (m: IArtifact) => void) {
          const model = clone(this.value);
          cb(model);
          this.$emit("input", model);
        }
      }
    })
    
    <app-dialog
      :title="title"
      :visible="visible"
      @action="save"
      @close="close">
    
      <artifact-create-form v-model="internal"/>
    
    </app-dialog>