如何在父事件上调用子组件上的函数


164

语境

在Vue 2.0中,文档和其他文档清楚地表明,父母与孩子之间的交流是通过道具进行的。

父母如何通过道具告诉孩子发生了什么事?

我应该只看一个叫做活动的道具吗?这感觉不对,也没有其他选择($emit/ $on代表孩子到父母,而中心模型代表远处的元素)。

我有一个父容器,它需要告诉其子容器可以在API上执行某些操作。我需要能够触发功能。

Answers:


233

给子组件赋予a ref并用于$refs直接在子组件上调用方法。

的HTML:

<div id="app">
  <child-component ref="childComponent"></child-component>
  <button @click="click">Click</button>  
</div>

javascript:

var ChildComponent = {
  template: '<div>{{value}}</div>',
  data: function () {
    return {
      value: 0
    };
  },
  methods: {
    setValue: function(value) {
        this.value = value;
    }
  }
}

new Vue({
  el: '#app',
  components: {
    'child-component': ChildComponent
  },
  methods: {
    click: function() {
        this.$refs.childComponent.setValue(2.0);
    }
  }
})

有关更多信息,请参阅refs上的Vue文档


13
这样,父级和子级组件将耦合在一起。对于真实事件,比如说当您不能仅更改触发动作的道具时,我会使用@Roy J建议的公交解决方案
Jared

3
对文档的参考以及vuejs.org/v2/guide/…–
ctf0

在子组件中,出于特殊原因,我不得不使用v-once终止反应。因此,将道具从父母传递给孩子的做法是不可行的,因此此解决方案确实可行!
约翰

1
新手问题:为什么使用ref而不是创建道具,而是先观察其值然后将其发送给父级的另一个函数? 我的意思是确实有很多事情要做,但是使用它是否ref安全?谢谢
Irfandy Jip

5
@IrfandyJip-是的,ref很安全。通常,不建议这样做,因为Vue社区更喜欢将状态传递给孩子,并将事件传递给父母。一般来说,这会导致更孤立的内部一致的组件(一件好事™)。但是,如果您要传递给孩子的信息确实是事件(或命令),则修改状态不是正确的模式。在这种情况下,使用a调用方法ref是完全可以的,并且不会崩溃或发生任何事情。
joerick

76

您所描述的是父级中的状态更改。您通过道具将其传递给孩子。正如您所建议的那样,您将使用watch该道具。当子级采取行动时,它会通过通知父级emit,然后父级可能会再次更改状态。

如果您确实希望将事件传递给孩子,则可以通过创建总线(只是Vue实例)并将其作为prop传递给孩子来实现


2
我认为这是符合官方Vue.JS样式指南和最佳做法的唯一答案。如果v-model在组件上使用了简写形式,则还可以通过使用更少的代码发出相应的事件来轻松重置该值。
Falco

例如,我想在用户单击按钮时发出警报。例如,您建议:-观看标志-发生点击时将此标志从0设置为1,-做一些事情-重置标志
Sinan Erdem

9
这非常不舒服,您必须prop在孩子中创建一个额外的对象,在中创建一个额外的属性data,然后添加watch...如果有内置的支持以某种方式将事件从父对象转移到孩子,这会很舒服。这种情况经常发生。
ИльяЗеленько

1
正如@ИльяЗеленько所说的那样,它的确经常发生,这将是一个天赐的礼物。
克雷格

1
谢谢@RoyJ,我想这虽然要求孩子在订阅时必须有巴士道具,但我认为在Vue中不建议将事件发送给孩子的整个想法。
Ben Winding

35

您可以使用$emit$on。使用@RoyJ代码:

的HTML:

<div id="app">
  <my-component></my-component>
  <button @click="click">Click</button>  
</div>

javascript:

var Child = {
  template: '<div>{{value}}</div>',
  data: function () {
    return {
      value: 0
    };
  },
  methods: {
    setValue: function(value) {
        this.value = value;
    }
  },
  created: function() {
    this.$parent.$on('update', this.setValue);
  }
}

new Vue({
  el: '#app',
  components: {
    'my-component': Child
  },
  methods: {
    click: function() {
        this.$emit('update', 7);
    }
  }
})

运行示例:https//jsfiddle.net/rjurado/m2spy60r/1/


6
我很惊讶它起作用。我认为向孩子发射是一种反模式,或者意图是仅从孩子到父母发射。换另一方向有潜在的问题吗?
jbodily

2
我不知道这可能不是最好的方法,但是如果您知道自己在做什么,那么事情就不成问题了。另一种方法是使用中央巴士:vuejs.org/v2/guide/...
drinor

14
这在孩子和父母之间造成了耦合,被认为是不当行为
morrislaptop

5
这仅起作用,因为父级不是组件,而是实际上是vue应用程序。实际上,这是使用vue实例作为总线。
Julio Rodrigues

2
@Bsienn对this。$ parent的调用使此组件依赖于父级。使用$ emit到props,因此唯一的依赖关系是通过Vue的通信系统。这种方法允许在组件层次结构中的任何地方使用相同的组件。
morrislaptop

6

如果有时间,请使用Vuex存储库监视变量(即状态)或直接触发(即调度)操作。


2
由于vuejs / vuex具有最佳反应性,因此在父级中进行更改vuex属性值的操作/变异,在子级中具有获得相同vuex $ store.state.property.value或“ watch”值的计算值vuex“ $ store.state.property.value”更改时执行某些操作的方法”
FabianSilva

6

不喜欢在期间使用子项中的绑定的事件总线方法。为什么?后续通话(我正在使用$oncreatecreatevue-router)多次绑定消息处理程序-导致每条消息有多个响应。

将道具从父母下放给孩子并将财产观察员放到孩子中的正统解决方案有点奏效更好。唯一的问题是孩子只能在价值转变上行动。多次传递相同的消息需要某种簿记来强制过渡,以便孩子可以接管零钱。

我发现,如果将消息包装在数组中,它将始终触发子观察程序-即使该值保持不变。

上级:

{
   data: function() {
      msgChild: null,
   },
   methods: {
      mMessageDoIt: function() {
         this.msgChild = ['doIt'];
      }
   }   
   ...
}

儿童:

{
   props: ['msgChild'],
   watch: {
      'msgChild': function(arMsg) {
         console.log(arMsg[0]);
      }
   }
}

HTML:

<parent>
   <child v-bind="{ 'msgChild': msgChild }"></child>
</parent>

1
我认为如果msgChild在父级上始终具有相同的状态,这将无法正常工作。例如:我想要一个打开模式的组件。父级不在乎当前状态是打开还是关闭,它只想随时打开模式。因此,如果父母这样做this.msgChild = true; 该模式已关闭,然后父级执行此操作。msgChild= true,该子级将不会收到该事件
Jorge Sainz

1
@JorgeSainz:这就是为什么在将值分配给数据项之前将其包装在数组中的原因。无需将值包装在数组中,它的行为就与您指定的一样。因此,msgChild = true,msgChild = true-无事件。msgChild = [true],msgChild = [true]-事件!
杰森·斯图尔特

1
我没看到 感谢您的澄清
Jorge Sainz '18

这很酷,但感觉有点破旧。我将使用它,因为它比使用组件ref hack更干净,并且比事件总线解决方案更简单。我知道vue希望解耦,并且只允许状态更改来影响组件,但是如果需要,应该有一些内置方法来调用子方法。也许是道具上的修改器,一旦它更改了状态,您可以自动将其重置为默认值,以便观察者为下一个状态更改做好准备。无论如何,感谢您发布您的发现。
克雷格

6

在子组件上调用方法的一种简单的分离方法是,从子组件中发出一个处理程序,然后从父组件中调用它。

var Child = {
  template: '<div>{{value}}</div>',
  data: function () {
    return {
      value: 0
    };
  },
  methods: {
  	setValue(value) {
    	this.value = value;
    }
  },
  created() {
    this.$emit('handler', this.setValue);
  }
}

new Vue({
  el: '#app',
  components: {
    'my-component': Child
  },
  methods: {
  	setValueHandler(fn) {
    	this.setter = fn
    },
    click() {
    	this.setter(70)
    }
  }
})
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.js"></script>

<div id="app">
  <my-component @handler="setValueHandler"></my-component>
  <button @click="click">Click</button>  
</div>

父级跟踪子处理程序的功能,并在必要时调用。


我喜欢这种解决方案的去向,但是父级中的“ this.setter”到底是什么?
克雷格

它是子组件作为处理程序事件的参数发出的setValue函数引用。
nilobarp '19

2

以下示例是自我解释。引用和事件可用于在父子之间调用函数。

// PARENT
<template>
  <parent>
    <child
      @onChange="childCallBack"
      ref="childRef"
      :data="moduleData"
    />
    <button @click="callChild">Call Method in child</button>
  </parent>
</template>

<script>
export default {
  methods: {
    callChild() {
      this.$refs.childRef.childMethod('Hi from parent');
    },
    childCallBack(message) {
      console.log('message from child', message);
    }
  }
};
</script>

// CHILD
<template>
  <child>
    <button @click="callParent">Call Parent</button>
  </child>
</template>

<script>
export default {
  methods: {
    callParent() {
      this.$emit('onChange', 'hi from child');
    },
    childMethod(message) {
      console.log('message from parent', message);
    }
  }
}
</script>

1

我认为我们应该考虑父母使用孩子方法的必要性。实际上,父母不必关心孩子的方法,而是可以将孩子组件视为FSA(有限状态机)。来控制子组件的状态。因此,观察状态变化或仅使用计算功能的解决方案就足够了


2
如果您说的是“父母永远不要控制孩子”,那么在某些情况下这是必要的。考虑一个倒数计时器组件。父母可能希望重置计时器以重新开始。仅使用道具是不够的,因为从time = 60到time = 60不会修改道具。计时器应公开一个“重置”功能,家长可以在适当时调用它。
tbm

0

您可以使用key使用key重新加载子组件

<component :is="child1" :filter="filter" :key="componentKey"></component>

如果要使用新的过滤器重新加载组件,请单击按钮以过滤子组件

reloadData() {            
   this.filter = ['filter1','filter2']
   this.componentKey += 1;  
},

并使用过滤器触发功能

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.