Event bubble / leak to grand parent issue
Vue version
3.2.38
Link to minimal reproduction
https://kasheftin.github.io/vue3-event-bubble-leak/
Steps to reproduce
- drag the item around
- watch (console.log) the
@change
event triggers every time when items are resorted, hovewer there's no$emit(change)
in the target component and the target component explicitly setsemits:['run']
.
What is expected?
Events should not leak
from the child component to the grand parent.
That's how it was in vue2.
What is actually happening?
@change
event bubble/leak happens every time when 3-rd party component triggers something inside.
System Info
No response
Any additional comments?
Basically there're 3 components: root
-> child
-> draggable
.
The root
component has @change
event listener.
The child
component has nothing and it explicitly sets emits: ['run']
meaning it can emit run
event only. It can not emit change
event.
The draggable
component is 3rd party. It does not matter how it works and what it emits. All that matter is it does not have @change
event listener in the child
component.
Basically, the vue2 logic was pretty straight-forward and simple. If a grand-child requires some property from grand-parent, it had to be specified in the intermediate component and passed down. The same happend with events - a child emits @change
event which is required to be caught on grand-parent, then the intermediate component had to have explicit @change=$emit('change', $event)
. It was a bit verbose, but it worked like a charm.
In Vue 3, all event listeners passed to a component which are not declared in that component's emits
, will be applied as native event listeners to the root element.
https://vuejs.org/guide/components/attrs.html#v-on-listener-inheritance
In Vue 2, you could create the same issue by adding the .native
listener:
<Child @change.native="handler" />
Now, is it a bit easier to accidentally pass a native event when you don't want to in Vue 3 compared to Vue 2? Yes. But it's not a bug, you can handle it yourself.
So that's how it works for now. We can think about changing it someday in Vue 4, but for the foreseeable future that's how it is.
Thanks a lot!
You are correct - if emits
option includes change
, then the @change
event does not leak to the grand parent. But a few questions still remain:
Since it worked differently in vue2, would not it be reasonable to implement a configurable setting defining if the non-specified event should pass through the component? There's inheritAttrs prop, it might be something like passChildEventsThrough default to true. For example, imagine there's a chain root -> parent -> child. Assume that root has
@event1
event listener defined, while parent hasemits=[event2]
. Then child emitsevent1
. It will be silently passed through to the root element. Basically, any third party can emit any event and there's no way to stop it from being passed.emits
option works like a black-list while I need a white-list so only specified events can go through.The https://kasheftin.github.io/vue3-event-bubble-leak/ code was updated.
emits
prop is not specified intentionally. The question is about this line: https://github.com/Kasheftin/vue3-event-bubble-leak/blob/61784ac38a3a13f9c38ef94bf45ddf8db50c6aa4/src/Test1.vue#L6. Some component has@change
event listener. It's natural to expect that anychange
event emitted inside will be caught by the listener. However this does not happen. The demo shows that while an item is being dragged, thechange
event popups up in the grand parent, going around change event listener inTest1.vue
. Then, only on mouseup, the correct change event happens. I would be grateful for any clarification of what's going on there.
About 1: I think ìnheritAttrs: false is allowing for what you want, pretty much.
All you would maybe need to do is filter any properties starting with on[A-Z]
from $attrs
and you have an object that contains only parent attributes, not parent listeners.
About 2: Seems like the implementation of that <draggable>
component triggers a native change
event -which bubbles up- at a different point in time before it emits a custom change
event. A general recommendation would be to not use event names that mirror native events that are also triggered. While I am aware you are not in control of this library's implementation, you could emit a differently named event yourself.
If you have additional questions towards this specific example, please use the repo's discussion feature or our discord to discuss it with the wider community.