Vuejs - 冒泡自定义事件
在组件中使用组件时,有没有办法让事件冒泡?
Is there a way to allow events to bubble up when using a component within a component?
我的应用程序是一个动态菜单.动态菜单是一个组件 (dyn-menu
),它为每个 <li>
menu-item)> 元素.每个 <menu-item>
都有一个与之关联的单击处理程序,该处理程序发出一个自定义事件(在完整实现中具有菜单项的 ID).但是应用程序看不到 <menu-item>
发出的事件,因为它们没有冒泡.
My application is a dynamic menu. The dynamic menu is a component (dyn-menu
) and it uses a local component (menu-item
) for each of the <li>
elements. Each <menu-item>
has a click handler associated with it that emits a custom event (with an ID for menu item in the full implementation). But the application doesn't see the events issued by <menu-item>
because they are not bubbled up.
有没有办法允许
组件本地的 <menu-item>
组件发出事件并且仍然允许 vapp
查看和处理事件吗?
Is there a way to allow the <menu-item>
component, which is local to the <dyn-menu>
component, emit the event and still allow vapp
to see and handle the event?
我对 Vuejs 很陌生,所以我可能会遗漏一些明显的东西.我可能试图通过使用两个组件来解决这个问题,但这不是处理它的最佳方法.有没有更好的方法来解决它?
I'm pretty new to Vuejs so I might be missing something obvious. And it's possible that I'm trying to solve this by using two components and that's not the best way to handle it. Is there is a better way to approach it?
这是一个 jsfiddle.您必须删除 <dyn-menu>
模板中的 @dyn-menu-item-click='itemClick'
行以说明事件不会冒泡如果组件不处理事件,则向上.如果删除该行,则 <dyn-menu>
不会处理该事件,但 vapp
也不会看到该事件.
Here's a jsfiddle. You have to remove the @dyn-menu-item-click='itemClick'
line in the <dyn-menu>
template to illustrate that the event doesn't bubble up if the component doesn't handle the event. If that line is removed then <dyn-menu>
doesn't handle the event but vapp
never sees the event either.
推荐答案
我知道有 4 个选项
- 像你一样重新发送事件
- 在子组件上(重复地)使用
this.$parent
来访问所需的父组件并发出事件.(请参阅下面的实现您自己的冒泡事件插件") - 使用由父级
提供
并在子级中注入
的事件总线. - 使用 Vuex 存储并将事件推送到子组件中的事件队列.在应用程序的其他地方,观察新元素的反应式事件队列或将其绑定到某个东西.
- Re-emit events like you did
- Use
this.$parent
(repetitively) on the child component to access the desired parent and emit the event. (see "Implement your own bubbling event plugin" below) - Use an event bus that is
provide
d by the parent andinject
ed in the children. - Use a Vuex store and push events to an event queue in the child component. Somewhere else in the app, watch that reactive event queue for new elements or just bind it to something.
实现自己的冒泡事件插件
这很简单.该插件添加了一个新的 $bubble
方法,该方法向其父级发出冒泡事件.我考虑过发布一个插件来做到这一点,但它是如此简单,以至于开销不值得.
Implement your own bubbling event plugin
It's very simple. The plugin adds a new $bubble
method that emits events that bubble to their parents. I considered publishing a plugin that does this, but it's so simple that the overhead is not worth it.
// Add this as a Vue plugin
Vue.use((Vue) => {
Vue.prototype.$bubble = function $bubble(eventName, ...args) {
// Emit the event on all parent components
let component = this;
do {
component.$emit(eventName, ...args);
component = component.$parent;
} while (component);
};
});
// Some nested components as an example
// note usage of "$bubble" instead of "$emit"
Vue.component('component-c', {
template: `
<button type="button" @click="$bubble('my-event', 'payload')">
Emit bubbling event
</button>`,
});
Vue.component('component-b', {
template: `<component-c @my-event="onMyEvent" />`,
methods: {
onMyEvent(...args) {
console.log('component-b listener: ', ...args);
},
},
});
Vue.component('component-a', {
template: `<component-b @my-event="onMyEvent" />`,
methods: {
onMyEvent(...args) {
console.log('component-a listener: ', ...args);
},
},
});
var vapp = new Vue({
el: '#app',
methods: {
onMyEvent(...args) {
console.log('root listener: ', ...args);
},
},
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<component-a @my-event="onMyEvent" />
</div>
事件总线如下所示:
Vue.component('dyn-menu', {
components: {
'menu-item': {
template: '<li @click="itemClick">{{item.text}}</li>',
props: ['item'],
inject: ['eventBus'], // <-- Inject in the child
methods: {
itemClick() {
// Emit the event on the event bus
this.eventBus.$emit('dyn-menu-item-click', ['menu-item dyn-menu-item-click']);
}
}
}
},
// ...
});
var vapp = new Vue({
el: '#app',
data: {
// ...
eventBus: new Vue(),
},
provide() {
return {
// The parent component provides the event bus to its children
eventBus: this.eventBus,
};
},
created() {
// Listen to events on the event bus
this.eventBus.$on('dyn-menu-item-click', this.menuClick);
},
methods: {
menuClick(message) {}
}
})
工作示例:https://jsfiddle.net/7vwfx52b/
这里列出了很多事件总线插件:https://github.com/vuejs/awesome-vue#custom-events
There are plenty of event bus plugins listed here: https://github.com/vuejs/awesome-vue#custom-events
相关文章