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

上下文

在Vue 2.0中,文档和其他人清楚地指出,从父母到孩子的通信是通过道具发生的。

问题

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

我该看个道具节目吗?这感觉不对,替代方法也不对($emit/$on是针对子到父的,而hub模型是针对远端元素的)。

例子

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

255555 次浏览

您所描述的是父进程状态的变化。你把它通过道具传递给孩子。正如你所建议的,你将watch那个道具。当子进程执行操作时,它会通过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;
}
}
})
<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>

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

你可以使用$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 3组合API

为子组件创建ref,在模板中赋值,并使用<ref>.value直接调用子组件。

<script setup>
import {ref} from 'vue';


const childComponentRef = ref(null);


function click() {
// `childComponentRef.value` accesses the component instance
childComponentRef.value.doSomething(2.0);
}
</script>


<template>
<div>
<child-component ref="childComponentRef" />
<button @click="click">Click me</button>
</div>
</template>

有几件事需要注意

  • 如果你的子组件使用<script setup>,你需要使用defineExpose声明公共方法(例如上面的doSomething)。
  • 如果你使用的是Typescript,详细的类型注释方法是在这里

Vue 3选项API / Vue 2

给子组件一个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);
}
}
})

有关更多信息,请参见Vue 3文档中的组件引用Vue 2关于裁判的文档

如果你有时间,使用Vuex存储来观察变量(即状态)或直接触发(即分派)一个动作。

不喜欢在create期间在子对象中使用$on绑定的事件总线的方法。为什么?随后的create调用(我正在使用vue-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>

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

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

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>

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

下面的例子是不言自明的。引用和事件可以用来调用函数从父和子。

// 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>

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

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

如果要重新加载带有新过滤器的组件,如果按钮单击过滤子组件

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

并使用过滤器来触发该函数

在父组件中调用子组件

<component :is="my_component" ref="my_comp"></component>
<v-btn @click="$refs.my_comp.alertme"></v-btn>

在子组件中

mycomp.vue

    methods:{
alertme(){
alert("alert")
}
}

您可以通过切换父节点中的布尔道具来模拟向子节点发送事件。

父代码:

...
<child :event="event">
...
export default {
data() {
event: false
},
methods: {
simulateEmitEventToChild() {
this.event = !this.event;
},
handleExample() {
this.simulateEmitEventToChild();
}
}
}

子代码:

export default {
props: {
event: {
type: Boolean
}
},
watch: {
event: function(value) {
console.log("parent event");
}
}
}