Javascript VueJs 2.0 从孙子组件发出事件到他的祖父组件
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/42615445/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
VueJs 2.0 emit event from grand child to his grand parent component
提问by BassMHL
It seems that Vue.js 2.0 doesn't emit events from a grand child to his grand parent component.
似乎 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'
})
This JsFiddle solves the issue https://jsfiddle.net/y5dvkqbd/4/, but by emtting two events:
这个 JsFiddle 解决了这个问题https://jsfiddle.net/y5dvkqbd/4/,但通过两个事件:
- One from grand child to middle component
- Then emitting again from middle component to grand parent
- 一个从孙子到中间组件
- 然后再次从中间组件发射到祖父
Adding this middle event seems repetitive and unneccessary. Is there a way to emit directly to grand parent that I am not aware of?
添加这个中间事件似乎是重复和不必要的。有没有办法直接发送给我不知道的祖父母?
采纳答案by BassMHL
NEW ANSWER (Nov-2018 update)
新答案(2018 年 11 月更新)
I discovered that we could actually do this by leveraging the $parentproperty in the grand child component:
我发现我们实际上可以通过利用$parent大子组件中的属性来做到这一点:
this.$parent.$emit("submit", {somekey: somevalue})
Much cleaner and simpler.
更干净,更简单。
回答by Bert
The Vue community generally favors using Vuex to solve this kind of issue. Changes are made to Vuex state and the DOM representation just flows from that, eliminating the need for events in many cases.
Vue 社区普遍倾向于使用 Vuex 来解决此类问题。对 Vuex 状态进行了更改,而 DOM 表示正是由此而来,在许多情况下消除了对事件的需要。
Barring that, re-emitting would probably be the next best choice, and lastlyyou might choose to use an event bus as detailed in the other highly voted answer to this question.
除此之外,重新发送可能是下一个最佳选择,最后您可能会选择使用事件总线,如该问题的另一个高票答案中所述。
The answer below is my original answer to this question and is not an approach I would take now, having more experience with Vue.
下面的答案是我对这个问题的原始回答,不是我现在会采用的方法,因为有了更多的 Vue 经验。
This is a case where I might disagree with Vue's design choice and resort to DOM.
在这种情况下,我可能不同意 Vue 的设计选择并求助于 DOM。
In grand-child,
在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);
}
}
}
and in parent,
并且在parent,
mounted(){
this.$el.addEventListener("eventtriggered", () => this.performAction())
}
Otherwise, yes, you have to re-emit, or use a bus.
否则,是的,您必须重新发射或使用总线。
Note: I added code in the doEvent method to handle IE; that code could be extracted in a reusable way.
注意:我在doEvent方法中添加了处理IE的代码;该代码可以以可重用的方式提取。
回答by Michael Rush
Vue 2.4 introduced a way to easily pass events up the hierarchy using vm.$listeners
Vue 2.4 引入了一种使用以下方法轻松地将事件向上传递到层次结构的方法 vm.$listeners
From https://vuejs.org/v2/api/#vm-listeners:
来自https://vuejs.org/v2/api/#vm-listeners:
Contains parent-scope
v-onevent listeners (without.nativemodifiers). This can be passed down to an inner component viav-on="$listeners"- useful when creating transparent wrapper components.
包含父作用域
v-on事件侦听器(无.native修饰符)。这可以通过以下方式传递给内部组件v-on="$listeners"- 在创建透明包装组件时很有用。
See the snippet below using v-on="$listeners"in the grand-childcomponent in the childtemplate:
请参阅以下在模板v-on="$listeners"中的grand-child组件中使用的片段child:
Vue.component('parent', {
template: '<div><p>I am the parent. The value is {{displayValue}}.</p> <child @toggle-value="toggleValue"></child></div>',
data(){
return {
value: false
}
},
methods: {
toggleValue() { this.value = !this.value }
},
computed: {
displayValue(){
return (this.value ? "ON" : "OFF")
}
}
})
Vue.component('child', {
template: '<div class="child"><p>I am the child. I\'m just a wrapper providing some UI.</p><grand-child v-on="$listeners"></grand-child></div>'
})
Vue.component('grand-child', {
template: '<div><p>I am the grand-child: <button @click="emitToggleEvent">Toggle the value</button></p></div>',
methods: {
emitToggleEvent() { this.$emit('toggle-value') }
}
})
new Vue({
el: '#app'
})
.child {
padding: 10px;
border: 1px solid #ddd;
background: #f0f0f0
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<parent></parent>
</div>
回答by Sly_cardinal
Yes, you're correct events only go from child to parent. They don't go further, e.g. from child to grandparent.
是的,你是正确的事件只会从孩子到父母。他们不会走得更远,例如从孩子到祖父母。
The Vue documentation (briefly) addresses this situation in the Non Parent-Child Communicationsection.
Vue 文档(简要)在非父子通信部分解决了这种情况。
The general idea is that in the grandparent component you create an empty Vuecomponent that is passed from grandparent down to the children and grandchildren via props. The grandparent then listens for events and grandchildren emit events on that "event bus".
总体思路是在祖父组件中创建一个空Vue组件,该组件通过 props 从祖父组件向下传递给子组件和孙组件。然后祖父母侦听事件,孙辈在该“事件总线”上发出事件。
Some applications use a global event bus instead of a per-component event bus. Using a global event bus means you will need to have unique event names or namespacing so events don't clash between different components.
某些应用程序使用全局事件总线而不是每个组件的事件总线。使用全局事件总线意味着您需要具有唯一的事件名称或命名空间,以便事件不会在不同组件之间发生冲突。
Here is an example of how to implement a simple global event bus.
下面是一个如何实现简单全局事件总线的示例。
回答by Sphinx
Another solution will be on/emit at rootnode:
另一个解决方案将在根节点上打开/发出:
Uses vm.$root.$emitin grand-child, then uses vm.$root.$onat the ancestor (or anywhere you'd like).
用途vm.$root.$emit在盛大的孩子,然后使用vm.$root.$on在祖先(或者任何你想)。
Updated: sometimes you'd like to disable the listener at some specific situations, use vm.$off(for example: vm.$root.off('event-name')inside lifecycle hook=beforeDestroy).
更新:有时您想在某些特定情况下禁用侦听器,请使用vm.$off(例如: vm.$root.off('event-name')inside Lifecyclehook= beforeDestroy)。
Vue.component('parent', {
template: '<div><button @click="toggleEventListener()">Listener is {{eventEnable ? "On" : "Off"}}</button>I am the parent - {{ action }} <child @eventtriggered="performAction"></child></div>',
data(){
return {
action: 1,
eventEnable: false
}
},
created: function () {
this.addEventListener()
},
beforeDestroy: function () {
this.removeEventListener()
},
methods: {
performAction() { this.action += 1 },
toggleEventListener: function () {
if (this.eventEnable) {
this.removeEventListener()
} else {
this.addEventListener()
}
},
addEventListener: function () {
this.$root.$on('eventtriggered1', () => {
this.performAction()
})
this.eventEnable = true
},
removeEventListener: function () {
this.$root.$off('eventtriggered1')
this.eventEnable = false
}
}
})
Vue.component('child', {
template: '<div>I am the child <grand-child @eventtriggered="doEvent"></grand-child></div>',
methods: {
doEvent() {
//this.$emit('eventtriggered')
}
}
})
Vue.component('grand-child', {
template: '<div>I am the grand-child <button @click="doEvent">Emit Event</button></div>',
methods: {
doEvent() { this.$root.$emit('eventtriggered1') }
}
})
new Vue({
el: '#app'
})
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<div id="app">
<parent></parent>
</div>
回答by digout
If you want to be flexible and simply broadcast an event to all parents and their parents recursively up to the root, you could do something like:
如果您想灵活地向所有父母及其父母递归地广播一个事件,直到根,您可以执行以下操作:
let vm = this.$parent
while(vm) {
vm.$emit('submit')
vm = vm.$parent
}
回答by rogervila
VueJS 2 components have a $parentproperty that contains their parent component.
VueJS 2 组件有一个$parent包含其父组件的属性。
That parent component also includes its own $parentproperty.
该父组件还包括它自己的$parent属性。
Then, accessing the "grandparent" component it's a matter of accessing the "parent's parent" component:
然后,访问“祖父”组件就是访问“父的父”组件的问题:
this.$parent["$parent"].$emit("myevent", { data: 123 });
Anyway, this is kinda tricky, and I recommend using a global state manager like Vuex or similar tools, as other responders have said.
无论如何,这有点棘手,我建议使用像 Vuex 或类似工具这样的全局状态管理器,正如其他响应者所说。
回答by kubaklamca
I've made a short mixin based on @digout answer. You want to put it, before your Vue instance initialization (new Vue...) to use it globally in project. You can use it similarly to normal event.
我根据@digout 的回答做了一个简短的 mixin。你想把它放在你的 Vue 实例初始化(新 Vue...)之前,以便在项目中全局使用它。您可以像普通事件一样使用它。
Vue.mixin({
methods: {
$propagatedEmit: function (event, payload) {
let vm = this.$parent;
while (vm) {
vm.$emit(event, payload);
vm = vm.$parent;
}
}
}
})
回答by roli roli
This is the only case when I use event bus!! For passing data from deep nested child, to not directly parent, communication.
这是我使用事件总线的唯一情况!!用于将数据从深层嵌套的子级传递到非直接父级的通信。
First: Create a js file (I name it eventbus.js) with this content:
首先:使用以下内容创建一个 js 文件(我将其命名为 eventbus.js):
import Vue from 'vue'
Vue.prototype.$event = new Vue()
Second: In your child component emit an event:
第二:在您的子组件中发出一个事件:
this.$event.$emit('event_name', 'data to pass')
Third: In the parent listen to that event:
第三:在父级中收听该事件:
this.$event.$on('event_name', (data) => {
console.log(data)
})
Note:If you don't want that event anymore please unregister it:
注意:如果您不想再参加该活动,请取消注册:
this.$event.$off('event_name')
INFO: No need to read the below personal opinion
信息:无需阅读以下个人意见
I don't like to use vuex for grand-child to grand-parent communication (Or similar communication level).
我不喜欢将 vuex 用于孙子到祖父的通信(或类似的通信级别)。
In vue.js for passing data from grand-parent to grand-child you can use provide/inject. But there is not something similar for the opposite thing. (grand-child to grand-parent) So I use event bus whenever I have to do that kind of communication.
在 vue.js 中将数据从祖父母传递给孙子女,您可以使用provide/inject。但是相反的东西没有类似的东西。(孙子到祖父母)所以每当我必须进行这种通信时,我都会使用事件总线。
回答by fylzero
I really dig the way this is handled by creating a class that is bound to the window and simplifying the broadcast/listen setup to work wherever you are in the Vue app.
我真的通过创建一个绑定到窗口的类并简化广播/监听设置来处理这种处理方式,以便在 Vue 应用程序中的任何位置工作。
window.Event = new class {
constructor() {
this.vue = new Vue();
}
fire(event, data = null) {
this.vue.$emit(event, data);
}
listen() {
this.vue.$on(event, callback);
}
}
Now you can just fire / broadcast / whatever from anywhere by calling:
现在你可以通过调用从任何地方发射/广播/任何东西:
Event.fire('do-the-thing');
...and you can listen in a parent, grandparent, whatever you want by calling:
...您可以通过以下方式听取父母,祖父母的意见,无论您想要什么:
Event.listen('do-the-thing', () => {
alert('Doing the thing!');
});

