我的vue应用程序使用:
由component-child
组成的组件父组件在component-parent里面我有按钮,当有人点击我想发出一个事件的按钮,以便由vue处理并传递给另一个组件
到目前为止我做了什么:
var vm = new Vue({
el: '#app',
methods:{
itemSelectedListener: function(item){
console.log('itemSelectedListener', item);
}
}
});
Vue.component('component-child', {
template: ' <span v-on:click="chooseItem(pty )" >Button </span>'
,
methods: {
chooseItem: function(pty){
console.log(pty);
this.$emit('itemSelected', {
'priority' : pty
});
}
}
});
Vue.component('component-parent', {
template: '<component-child v-for="q in items" ></component-child>'
});
HTML:
<component-parent v-on:itemSelected="itemSelectedListener" ></component-parent>
它到达我的console.log(pty);行但似乎这个。$ emit(&#39; itemSelected&#39;不会通过:
console.log(&#39; itemSelectedListener&#39;,item); //这不会被称为......
提示?
我应该从child-&gt; parent-&gt; Vue-instance冒泡事件? (我也试过但没有成功)
答案 0 :(得分:12)
component-parent
模板在尝试呈现多个子组件时存在一个问题。 Vue通常需要在组件内部使用单个根div,因此您需要将其包装在div或其他标记中。
<div>
<component-child v-for="q in items"></component-child>
</div>
要指出的第二件事是你从子组件中发出一个事件,该事件是2级,你在根目录中听它。
Root //but you listen to the event up here 1 level above
Component 1 //you should listen to the event here
Component 2 //your try to emit it from here
这里有2个选项。即使在component-child
中,也可以从component-parent
发出,然后向上传播。小提琴https://jsfiddle.net/bjqwh74t/29/
第二个选项是注册一个全局所谓的bus
,它是一个空的vue实例,当你想要非子父组件之间的通信时,你可以使用这个实例。小提琴https://jsfiddle.net/bjqwh74t/30/
通常在父组件和子组件之间通过从子组件发出并使用v-on:event-name="handler"
在父组件中进行侦听来直接使用事件,但对于组件之间有更多级别的情况,则使用第二种方法。
第一个案例的文档链接:https://vuejs.org/v2/guide/components.html#Using-v-on-with-Custom-Events
第二种情况的文档链接:https://vuejs.org/v2/guide/components.html#Non-Parent-Child-Communication
PS:更喜欢将kebab-case用于事件名称,这意味着您使用-
而不是大写字母进行书写。使用大写字母书写可能会导致您的事件未被根目录捕获的奇怪情况。
答案 1 :(得分:5)
对于它的价值,您可以使用浏览器的事件API。与Vue的内置内容相比,它需要的脚本要多一些,但是它也可以带您解决这些冒泡的问题(与公认的答案中的代码和创建“总线”的代码量差不多)。
关于子组件:
this.$el.dispatchEvent(new CustomEvent('itemSelected', { detail: { 'priority' : pty }, bubbles: true, composed: true });
在父组件上的mounted
生命周期部分:
mounted() {
this.$el.addListener('itemSelected', e => console.log('itemSelectedListener', e.detail));
}
答案 2 :(得分:1)
有点晚了但是我的表现如何:
组件的子:
this.$root.$emit('foobar',{...});
组件的父:
this.$root.$on('foobar')
答案 3 :(得分:1)
在您的子组件中,只需使用$emit
即可将事件发送到$root
,如下所示:
v-on:click="$root.$emit('hamburger-click')"
然后,在您的父组件(例如:“ App”)中,在Vue mounted
生命周期挂钩中设置侦听器,如下所示:
export default {
<snip...>
mounted: function() {
this.$root.$on('hamburger-click', function() {
console.log(`Hamburger clicked!`);
});
}
}
答案 4 :(得分:1)
我很惊讶没有人建议使用事件总线组件。在高度分离的系统中,这是一种很常见的模式,它具有共享的事件总线,然后使用它将多个断开连接的组件链接到pub / sub样式中。
//eventbus.js
import Vue from 'vue'
export const EventBus = new Vue()
一旦拥有一个,就可以从任何地方发布事件很简单
// component1.js
import { EventBus } from '@/services/eventbus'
...
EventBus.$emit('do-the-things')
从其他地方听他们的话。
// component2.js
import { EventBus } from '@/services/eventbus'
...
EventBus.$on('do-the-things', this.doAllTheThings)
请注意,这两个组件彼此之间一无所知,并且它们实际上并不需要关心引发事件的方式或原因。
此方法存在一些潜在的不良副作用。事件名称确实需要在全局范围内唯一,以免混淆应用程序。除非您执行更复杂的操作,否则您还可能通过单个对象引导每个事件。您可以在自己的应用程序源上进行成本/收益分析,以查看是否适合您。
答案 5 :(得分:-1)
创建一个custome directive进行冒泡并在子组件中使用它。
示例指令:
// Add this to main.ts when initializing Vue
Vue.directive('bubble', {
bind(el, { arg }, {context, componentInstance}) {
if (!componentInstance || !context || !arg) {
return;
}
// bubble the event to the parent
componentInstance.$on(v, context.$emit.bind(context, arg));
}
});
子组件可以使用该指令通过父组件发出指令(我切换为kabob大小写和事件的简写形式)。
<!-- template for component-parent -->
<component-child v-bubble:item-selected v-for="q in items"></component-child>
<!-- usage of component-parent -->
<component-parent @:item-selected="itemSelectedListener"></component-parent>
在下面包括我的完全绑定指令。它允许冒泡多个事件。
<!--------------------
* A few examples
--------------------->
<!-- bubble single event -->
<child v-bubble:click/>
<child v-bubble:_.click/>
<child v-bubble="'click'"/>
<child v-bubble:any-costume-event-will-work/>
<!-- bubble: click, focus, blur -->
<child v-bubble:_.click.focus.blur/>
<child v-bubble="'click, focus, blur'"/>
<!-- prefixed bubbling: click, focus, blur as child-click, child-focus, child-blur -->
<child v-bubble:child.click.focus.blur/>
<child v-bubble:child="'click, focus, blur'"/>
Vue.directive('bubble', {
bind(el, { value, arg: prefix = '', modifiers }, {context, componentInstance}) {
const events = value && value.trim() ? value.split(',') : Object.keys(modifiers);
if (!events.length && prefix) {
events.push(prefix);
prefix = '';
} else if(prefix) {
prefix = prefix === '_' ? '' : prefix += '-';
}
if (!componentInstance || !context || !events.length) {
return;
}
events.forEach((v: string) => {
v = v.trim();
const eventName = `${prefix}${v}`;
const bubble = context.$emit.bind(context, eventName);
componentInstance.$on(v, bubble);
});
}
});