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

jbo*_*ily 116 javascript event-handling vue.js vuejs2

上下文

在Vue 2.0中,文档和其他人清楚地表明,从父母到孩子的沟通是通过道具进行的.

父母如何告诉孩子一个事件是通过道具发生的?

我应该只看一个名为事件的道具吗?这感觉不对,也没有替代方案($emit/ $on是孩子到父母,而中心模型是远程元素).

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

joe*_*ick 153

给子组件a ref并使用$refs直接调用子组件上的方法.

HTML:

<div id="app">
  <child-component ref="childComponent"></child-component>
  <button @click="click">Click</button>  
</div>
Run Code Online (Sandbox Code Playgroud)

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);
    }
  }
})
Run Code Online (Sandbox Code Playgroud)

有关更多信息,请参阅参考文献中的Vue文档.

  • 这样父母和子组件就会耦合.对于真实事件,比如当你不能只改变一个道具触发一个动作时,我会选择@Roy J建议的总线解决方案 (9认同)
  • @IrfandyJip - 是的,`ref` 是安全的。通常,不鼓励这样做,因为 Vue 社区更喜欢将状态传递给子级,并将事件传递回父级。一般来说,这会导致更孤立的、内部一致的组件(一件好事™)。但是,如果您传递给孩子的信息确实是一个*事件*(或一个命令),那么修改状态就不是正确的模式。在这种情况下,使用 `ref` 调用一个方法是完全没问题的,它不会崩溃或发生任何事情。 (7认同)
  • @Jared 所有应用程序都需要某种程度的耦合。通常,如果可以简化代码,父级和子级之间的小型直接耦合就很好 - 当所需要的只是单个函数调用时,引入整个消息总线可能会给系统带来过多的复杂性 (4认同)
  • 参考文档将是一个有用的以及https://vuejs.org/v2/guide/components-edge-cases.html#Accessing-Child-Component-Instances-amp-Child-Elements (3认同)

Roy*_*y J 62

你所描述的是父母的状态变化.你通过道具把它传给了孩子.正如你的建议,你会watch支持.当孩子采取行动时,它会通过a通知父母emit,然后父母可能会再次改变状态.

var Child = {
  template: '<div>{{counter}}</div>',
  props: ['canI'],
  data: function () {
    return {
      counter: 0
    };
  },
  watch: {
    canI: function () {
      if (this.canI) {
        ++this.counter;
        this.$emit('increment');
      }
    }
  }
}
new Vue({
  el: '#app',
  components: {
    'my-component': Child
  },
  data: {
    childState: false
  },
  methods: {
    permitChild: function () {
      this.childState = true;
    },
    lockChild: function () {
      this.childState = false;
    }
  }
})
Run Code Online (Sandbox Code Playgroud)
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.2.1/vue.js"></script>
<div id="app">
<my-component :can-I="childState" v-on:increment="lockChild"></my-component>
<button @click="permitChild">Go</button>
</div>
Run Code Online (Sandbox Code Playgroud)

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

  • 这是非常不舒服的,你必须在一个孩子中创建一个额外的"prop",在`data`中创建一个额外的属性,然后添加`watch` ...如果内置支持以某种方式从父母对孩子.这种情况经常发生. (4认同)
  • 我认为这是符合官方Vue.JS样式指南和最佳做法的唯一答案。如果在组件上使用简写的“ v-model”,则还可以通过使用更少的代码发出相应的事件来轻松重置值。 (2认同)
  • 正如@ИльяЗеленько所说,这种情况确实经常发生,现在这将是天赐之物。 (2认同)

dri*_*nor 26

你可以使用$emit$on.使用@RoyJ代码:

HTML:

<div id="app">
  <my-component></my-component>
  <button @click="click">Click</button>  
</div>
Run Code Online (Sandbox Code Playgroud)

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);
    }
  }
})
Run Code Online (Sandbox Code Playgroud)

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

  • 这会在孩子和父母之间产生耦合,被认为是不好的做法 (9认同)
  • 这只能起作用,因为父级不是组件,而是实际上是一个vue应用程序.实际上,这是使用vue实例作为总线. (5认同)
  • 我很惊讶它有效.我认为向孩子发射是一种反模式,或者意图是发射只是从孩子到父母.走另一条路有什么潜在的问题吗? (4认同)
  • @Bsienn 对 this.$parent 的调用使该组件依赖于父组件。使用 $emit to 和 props 因此唯一的依赖关系是通过 Vue 的通信系统。这种方法允许在组件层次结构中的任何位置使用相同的组件。 (3认同)
  • 这可能不被认为是最好的方式,我不知道,但如果你知道你在做什么我的事情不是问题.另一种方法是使用中央总线:https://vuejs.org/v2/guide/components.html#Non-Parent-Child-Communication (2认同)
  • 我认为在某些情况下,这比从父组件调用函数更好,因为通过这种方式,您可以管理声明它的组件内代码的执行,即使它在调用函数时创建与父组件的耦合父组件将控制权从子组件中夺走 (2认同)
  • 这在 Vue 3 上不起作用。Vue 3 删除了 $on 事件:https://v3.vuejs.org/guide/migration/events-api.html#_3-x-update (2认同)

nil*_*arp 13

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

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)
    }
  }
})
Run Code Online (Sandbox Code Playgroud)
<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>
Run Code Online (Sandbox Code Playgroud)

父进程跟踪子处理程序函数并在必要时调用。


Jas*_*art 9

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

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

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

家长:

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

孩子:

{
   props: ['msgChild'],
   watch: {
      'msgChild': function(arMsg) {
         console.log(arMsg[0]);
      }
   }
}
Run Code Online (Sandbox Code Playgroud)

HTML:

<parent>
   <child v-bind="{ 'msgChild': msgChild }"></child>
</parent>
Run Code Online (Sandbox Code Playgroud)


bri*_*t08 6

如果您有时间,请使用Vuex商店直接观察变量(也称为状态)或触发(也称为调度)操作.

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

Bal*_*aji 6

在父组件中调用子组件

<component :is="my_component" ref="my_comp"></component>
<v-btn @click="$refs.my_comp.alertme"></v-btn>
Run Code Online (Sandbox Code Playgroud)

在子组件中

mycomp.vue

    methods:{     
    alertme(){
            alert("alert")
            }
    }
Run Code Online (Sandbox Code Playgroud)


Muk*_*han 5

下面的例子是自我解释的。其中 refs 和 events 可用于从父和子调用函数。

// 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>
Run Code Online (Sandbox Code Playgroud)