VueJs 2.0从大孩子到他的大父母组件发出事件

似乎Vue.js 2.0不会将事件从孙子传递到他的父组件。

Vue.component('parent', {
  template: '<div>I am the parent - {{ action }} <child @eventtriggered="performAction"></child></div>',
  data(){
    return {
      action: 'No action'
    }
  },
  methods: {
    performAction() { this.action = 'actionDone' }
  }
})

Vue.component('child', {
  template: '<div>I am the child <grand-child></grand-child></div>'
})

Vue.component('grand-child', {
  template: '<div>I am the grand-child <button @click="doEvent">Do Event</button></div>',
  methods: {
    doEvent() { this.$emit('eventtriggered') }
  }
})

new Vue({
  el: '#app'
})

这个JsFiddle解决了https://jsfiddle.net/y5dvkqbd/4/的问题,但是通过列举了两个事件:

  • 从孙子到中间部分
  • 然后从中间部分再次发射到祖父母

添加此中间事件似乎是重复且不必要的。有没有一种方法可以直接向我不知道的祖父母发出?

Stafan番长2020/03/11 10:28:29

我根据@digout答案做了一个简短的mixin。您想在Vue实例初始化(新Vue ...)之前放置它,以便在项目中全局使用它。您可以像正常事件一样使用它。

Vue.mixin({
  methods: {
    $propagatedEmit: function (event, payload) {
      let vm = this.$parent;
      while (vm) {
        vm.$emit(event, payload);
        vm = vm.$parent;
      }
    }
  }
})
西门逆天2020/03/11 10:28:29

VueJS 2组件具有$parent包含其父组件属性。

该父组件还包括其自己的$parent属性。

然后,访问“祖父母”组件就是访问“父母的父母”组件的问题:

this.$parent["$parent"].$emit("myevent", { data: 123 });

无论如何,这有点棘手,我建议使用像其他响应者所说的那样的全局状态管理器,例如Vuex或类似的工具。

TomEva2020/03/11 10:28:29

是的,您是对的,事件只发生在孩子和父母之间。他们没有走得更远,例如从孩子到祖父母。

Vue文档(简要地)在“ 非父子沟通”部分中解决了这种情况

通常的想法是,在祖父母组件中创建一个空Vue组件,组件通过道具从祖父母那里传递给子孙。然后,祖父母会监听事件,孙子孙女会在该“事件总线”上发出事件。

一些应用程序使用全局事件总线而不是每个组件的事件总线。使用全局事件总线意味着您将需要具有唯一的事件名称或命名空间,以便事件不会在不同组件之间发生冲突。

这是一个如何实现简单的全局事件总线的示例

Tony宝儿2020/03/11 10:28:29

Vue社区通常支持使用Vuex解决此类问题。对Vuex状态进行了更改,并且DOM表示由此而来,从而在许多情况下无需发生事件。

除非如此,否则重新发送可能是下一个最佳选择,最后,您可能会选择使用事件总线,如对该问题的其他最高评价的答案中所述。

下面的答案是我对这个问题的原始答案,而不是我现在拥有Vue经验的方法。


在这种情况下,我可能会不同意Vue的设计选择,而诉诸DOM。

grand-child

methods: {
    doEvent() { 
        try {
            this.$el.dispatchEvent(new Event("eventtriggered"));
        } catch (e) {
            // handle IE not supporting Event constructor
            var evt = document.createEvent("Event");
            evt.initEvent("eventtriggered", true, false);
            this.$el.dispatchEvent(evt);
        }
    }
}

parent

mounted(){
    this.$el.addEventListener("eventtriggered", () => this.performAction())
}

否则,是的,您必须重新发射或使用公共汽车。

注意:我在doEvent方法中添加了代码来处理IE;该代码可以以可重用的方式提取。

Near米亚2020/03/11 10:28:29

新答案(2018年11月更新)

我发现实际上可以通过利用$parent大子组件中属性来做到这一点

this.$parent.$emit("submit", {somekey: somevalue})

更清洁,更简单。