>上下文
在 Vue 2.0 中,文档和其他文档清楚地表明,父级与子级之间的通信是通过 props 进行的。
问题
父母如何通过道具告诉孩子事件发生了?
我应该只看一个叫做事件的道具吗?这感觉不对,替代方案也不对($emit
/$on
是针对子项到父项的,而集线器模型是针对远处元素的)。
例
我有一个父容器,它需要告诉其子容器可以在 API 上执行某些操作。我需要能够触发函数。
Vue 3 Composition 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>
,则需要声明公共方法(例如doSomething
上文)使用defineExpose
. - 如果您使用的是 Typescript,有关如何键入注释的详细信息,请参阅此处。
Vue 3 Options 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 文档 关于组件 refs 或 Vue 2 文档 关于 refs。
您描述的是父级状态的变化。你通过道具把它传递给孩子。正如你所建议的,你会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/
在子组件上调用方法的一种简单的解耦方法是从子组件发出处理程序,然后从父组件调用它。
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>
父级跟踪子处理程序函数,并在必要时进行调用。
下面的示例是不言自明的。 其中 refs 和事件可用于从父项和子项调用函数。
// 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>
不喜欢在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>
如果你有时间,使用 Vuex 存储来监视变量(又名状态)或直接触发(又名调度)一个动作。
在父组件中调用子组件
<component :is="my_component" ref="my_comp"></component>
<v-btn @click="$refs.my_comp.alertme"></v-btn>
在子组件中
mycomp.vue
methods:{
alertme(){
alert("alert")
}
}
我认为我们应该考虑父母使用孩子方法的必要性。事实上,父母不需要关心子元的方法,而是可以将子组件视为FSA(有限状态机)。父组件,用于控制子组件的状态。因此,观察状态变化或仅使用计算功能的解决方案就足够
了您可以使用key使用key重新加载子组件
<component :is="child1" :filter="filter" :key="componentKey"></component>
如果要使用新过滤器重新加载组件,如果按钮单击过滤器子组件
reloadData() {
this.filter = ['filter1','filter2']
this.componentKey += 1;
},
并使用过滤器触发函数
您可以通过在父级中切换布尔道具来模拟向子级发送事件。
父代码 :
...
<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");
}
}
}