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

Javascript 如何使用Vue js 2在组件子组件链上冒泡事件?,javascript,vuejs2,Javascript,Vuejs2,我的vue应用程序使用: 组件父组件由组件子组件组成的组件 在组件父级中,我有一些按钮,当有人单击某个按钮时,我希望发出一个事件,以便由vue处理并传递给另一个组件 到目前为止我所做的: var vm = new Vue({ el: '#app', methods: { itemSelectedListener: function(item){ console.log('itemSelectedListener', item);

我的vue应用程序使用:

组件父组件由组件子组件组成的组件

在组件父级中,我有一些按钮,当有人单击某个按钮时,我希望发出一个事件,以便由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>'
});
暗示


我应该从child->parent->Vue实例冒泡事件吗?(我也试过了,但没有成功)

您的
组件父级
模板有一个问题,因为它试图渲染多个子组件。Vue通常在组件中需要一个根div,因此需要将其包装在div或其他标记中

<div>
    <component-child  v-for="q in items"></component-child>
</div>
你有两个选择。从
组件子级发出
组件父级
中侦听该事件,然后向上传播该事件。拨弄

第二个选项是注册一个全局的所谓的
总线
,它是一个空的vue实例,当您需要在非子父组件之间进行通信时,您可以将其用于此类情况。拨弄

通常,在父组件和子组件之间,您通过使用
v-on:event name=“handler”
从子组件发出并在父组件中侦听来直接使用事件,但对于组件之间级别更高的情况,您使用第二种方法

第一个案例的文档链接:

第二个案例的文档链接:


PS:事件名称更倾向于使用烤肉串大小写,这意味着您用
-
而不是大写字母书写。用大写字母书写可能会导致一些奇怪的情况,你的事件没有被记录在根中。

有点晚了,但我是这样做的:

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
子组件:

this.$root.$emit('foobar',{...});
组件父级:

this.$root.$on('foobar')

在子组件中,只需使用
$emit
$root
发送事件,如下所示:

v-on:click="$root.$emit('hamburger-click')"
  export default {
    <snip...>
    mounted: function() {
      this.$root.$on('hamburger-click', function() {
        console.log(`Hamburger clicked!`);
      });
    }
  }
然后,在父组件(例如:“应用程序”)中,在Vue
mounted
lifecycle钩子中设置侦听器,如下所示:

v-on:click="$root.$emit('hamburger-click')"
  export default {
    <snip...>
    mounted: function() {
      this.$root.$on('hamburger-click', function() {
        console.log(`Hamburger clicked!`);
      });
    }
  }
导出默认值{
挂载:函数(){
此.$root.$on('hamburger-click',函数(){
log(`Hamburger clicked!`);
});
}
}

您可以使用浏览器的事件API。它需要比Vue内置的东西多一点脚本编写,但它也可以帮助您解决这些冒泡的问题(与公认的答案中创建“总线”的代码量差不多)

关于子组件:

this.$el.dispatchEvent(new CustomEvent('itemSelected', { detail: { 'priority' : pty }, bubbles: true, composed: true });
在父组件上,在
装入的
生命周期部件中:

mounted() {
    this.$el.addEventListener('itemSelected', e => console.log('itemSelectedListener', e.detail));
}
为冒泡创建一个函数,并在子组件中使用它

指令样本:

// 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大小写和事件简写)


我有点惊讶没有人建议使用事件总线组件。在高度解耦的系统中,有一个共享的事件总线,然后使用它将多个断开连接的组件链接在一起,这是一种相当常见的模式

//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)
请注意,这两个组件互不了解任何信息,它们也不需要真正关心事件是如何或为什么引发的


这种方法有一些潜在的不良副作用。事件名称必须是全局唯一的,这样你就不会混淆你的应用程序。除非您做一些更复杂的事情,否则您还可能通过单个对象引导每个事件。您可以在自己的应用程序源上进行成本/收益分析,看看它是否适合您。

第二个案例的文档链接不再存在。您有更新的链接吗?我甚至可以说:Vue中的事件与浏览器中的本机事件不同。Vue中的事件作为对父级的回调,而子级中的$emit只是对父级回调的调用。我是对的?P.S.I翻阅了Vue中的所有文档,但在任何地方都没有明确说明。在我看来,他们把新手和“事件”这个词混淆了。我发现了一个很好的工作链接:“定制事件”和“事件总线”部分。我已经多次看到总线建议,并且在第三方模块中使用,但是,对于我和我的用例来说,使用总线不是正确的模式,要让它按照我想要的方式工作,需要做很多工作。这就是我想要的,很明显你已经说过了,但我想不起来。常规vue事件绑定v-bind:itemSelected有效在某些情况下,这是唯一的解决方案。我在Vue项目中使用了Rete框架,Rete也使用了Vue,但作为Vue组件的Rete节点没有$root和$parent,而本机CustomEvents是将事件传递给我的应用程序的唯一方法。但应该是这样的。$el.addEventListener('itemSelected',e=>console.log('itemSelectedListener',e.detail));addEventListener而不是AddListener这对我有用!简单的解决方案。我使用
v-on:itemSelected.native
使其工作。如果没有
.native
后缀,它就无法工作。谢谢@YamiOdymel我会修复您还需要调用
this.$root.$off('foorbar',fn)
中的
beforeDestroy()
destroy()
为避免内存泄漏,fn变量必须与
中的值匹配。$root.$on('foorbar',fn)
因此回调函数不能是匿名函数。注意:
$on
$off
已被弃用,因为Vue 3.0事件总线是更复杂组件层次结构的良好解决方案。但这与问题主题无关。@slf FYI,Cristi Jora在他的回答中已经提到了事件总线。这是一个用作中介的全局发布/订阅,从长远来看,它可以演变为一个god对象,并使组件与中介耦合,或至少其接口。在我看来,Vue应该提供事件冒泡。我看到这个解决方案被否决了,但没有任何评论说明原因。它是d
//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)