如何使用Vue js 2在组件子组件链上冒泡事件?

时间:2017-02-03 16:51:23

标签: javascript vuejs2

我的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冒泡事件? (我也试过但没有成功)

6 个答案:

答案 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);
    });
  }
});