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

IT技术 javascript vue.js event-handling vuejs2
2021-02-03 13:43:26

语境

在 Vue 2.0 中,文档和其他文件清楚地表明从父到子的通信是通过 props 发生的。

问题

父母如何通过props告诉其孩子发生了事件?

我应该只看一个叫做事件的props吗?这感觉不对,替代方案也不对($emit/$on用于孩子到父母,而集线器模型用于遥远的元素)。

例子

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

6个回答

给子组件一个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 文档

这样父组件和子组件就耦合起来了。对于真实事件,比如说当你不能仅仅改变道具来触发一个动作时,我会采用@Roy J 建议的总线解决方案
2021-03-17 13:43:26
@IrfandyJip - 是的,ref是安全的。通常,不鼓励这样做,因为 Vue 社区更喜欢将状态传递给孩子,并将事件传回给父母。一般来说,这会导致更孤立的、内部一致的组件(一件好事™)。但是,如果您传递给孩子的信息确实是一个事件(或命令),那么修改状态就不是正确的模式。在这种情况下,使用 a 调用方法ref完全没问题,并且不会崩溃或发生任何事情。
2021-03-18 13:43:26
@Jared 所有应用程序都需要某种程度的耦合。通常,父子之间的小的直接耦合如果能简化代码就可以了——当只需要一个函数调用时,引入整个消息总线可能会给系统带来多余的复杂性
2021-03-30 13:43:26
对文档的引用也很有帮助vuejs.org/v2/guide/...
2021-04-08 13:43:26
新手问题:为什么使用ref而不是创建一个 prop,它会监视它的值然后将它发送到 parent 中的另一个函数? 我的意思是它确实有很多事情要做,但是使用ref安全吗?谢谢
2021-04-09 13:43:26

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

如果你真的想将事件传递给孩子,你可以通过创建一个总线(它只是一个 Vue 实例)并将它作为 prop 传递给孩子来实现

正如@ИльяЗеленько 所说,它确实经常发生,现在这将是天赐之物。
2021-03-18 13:43:26
这是非常不舒服的,你必须prop在一个孩子中创建一个额外的,一个额外的属性data,然后添加watch......如果有内置的支持以某种方式将事件从父级转移到子级,那将会很舒服。这种情况经常发生。
2021-03-31 13:43:26
例如,我想在用户单击按钮时发出警报。例如,您是否建议: - 观看标志 - 发生点击时将此标志从 0 设置为 1, - 做某事 - 重置标志
2021-04-09 13:43:26
谢谢@RoyJ,我想这需要在孩子订阅它时存在 bus prop,我想在 Vue 中不鼓励将事件发送给孩子的整个想法。
2021-04-09 13:43:26
我认为这是符合官方 Vue.JS 风格指南和最佳实践的唯一答案。如果您v-model在组件上使用简写,您还可以通过用更少的代码发出相应的事件来轻松重置值。
2021-04-12 13:43:26

您可以使用$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/

这只是因为父级不是组件而是实际上是一个 vue 应用程序。实际上,这是使用 vue 实例作为总线。
2021-03-17 13:43:26
@Bsienn 对 this.$parent 的调用使这个组件依赖于父组件。使用 $emit to 和 props,所以唯一的依赖是通过 Vue 的通信系统。这种方法允许在组件层次结构中的任何地方使用相同的组件。
2021-03-21 13:43:26
这可能不被认为是最好的方法,我不知道,但是如果你知道你在做什么,我的事情就没有问题。另一种方式是使用中央总线:vuejs.org/v2/guide/...
2021-03-23 13:43:26
我很惊讶它有效。我认为向孩子发射是一种反模式,或者意图是让发射仅从孩子到父母。走另一条路有什么潜在的问题吗?
2021-03-29 13:43:26
这在孩子和父母之间造成了耦合,被认为是不好的做法
2021-03-30 13:43:26

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

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”究竟是什么?
2021-03-15 13:43:26
它是子组件发出的 setValue 函数引用,作为处理程序事件的参数。
2021-03-15 13:43:26

不喜欢的事件总线方式使用$on绑定在期间孩子create为什么?后续create调用(我正在使用vue-router)不止一次绑定消息处理程序——导致每条消息有多个响应。

通过props下来从父到子,并把财产观察者在孩子的正统解决方案工作一点更好。唯一的问题是孩子只能对value转变采取行动。多次传递相同的消息需要某种簿记来强制转换,以便孩子可以接受更改。

我发现如果我将消息包装在一个数组中,它总是会触发子观察者——即使值保持不变。

家长:

{
   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>
@JorgeSainz:这就是为什么我在将值分配给数据项之前将其包装在数组中的原因。不将值包装在数组中,它的行为与您指定的一样。所以,msgChild = true,msgChild = true——没有事件。msgChild = [true], msgChild = [true] -- 事件!
2021-03-16 13:43:26
我没有看到。感谢您的澄清
2021-04-03 13:43:26
这很酷,但感觉有点hackish。我将使用它,因为它比使用组件引用 hack 更干净,而且比事件总线解决方案更简单。我知道 vue 想要解耦并且只允许状态更改来影响组件,但是如果需要,应该有一些内置的方法来调用孩子的方法。也许 prop 上的一个修饰符,一旦它改变了状态,你就可以自动将它重置为默认值,以便观察者为下一次状态改变做好准备。无论如何,感谢您发布您的发现。
2021-04-03 13:43:26
我认为如果 msgChild 在父级上始终具有相同的状态,这将不起作用。例如:我想要一个打开模态的组件。父级并不关心当前状态是打开还是关闭,它只想随时打开模态。所以,如果父母这样做了 this.msgChild = true; 模态关闭,然后父执行this.msgChild = true,子不会收到事件
2021-04-11 13:43:26