Skip to content

Commit 6f8ea35

Browse files
authored
fix(runtime-vapor): should not fallthrough emit handlers to vdom child (#13500)
1 parent 88ef97f commit 6f8ea35

File tree

3 files changed

+61
-2
lines changed

3 files changed

+61
-2
lines changed

packages/runtime-core/src/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -557,3 +557,7 @@ export { startMeasure, endMeasure } from './profiling'
557557
* @internal
558558
*/
559559
export { initFeatureFlags } from './featureFlags'
560+
/**
561+
* @internal
562+
*/
563+
export { createInternalObject } from './internalObject'

packages/runtime-vapor/__tests__/componentAttrs.spec.ts

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
1-
import { type Ref, nextTick, ref } from '@vue/runtime-dom'
1+
import {
2+
type Ref,
3+
createApp,
4+
defineComponent,
5+
h,
6+
nextTick,
7+
ref,
8+
} from '@vue/runtime-dom'
29
import {
310
createComponent,
411
defineVaporComponent,
@@ -8,6 +15,7 @@ import {
815
setProp,
916
setStyle,
1017
template,
18+
vaporInteropPlugin,
1119
} from '../src'
1220
import { makeRender } from './_utils'
1321
import { stringifyStyle } from '@vue/shared'
@@ -361,4 +369,42 @@ describe('attribute fallthrough', () => {
361369
const el = host.children[0]
362370
expect(el.classList.length).toBe(0)
363371
})
372+
373+
it('should not fallthrough emit handlers to vdom child', () => {
374+
const VDomChild = defineComponent({
375+
emits: ['click'],
376+
setup(_, { emit }) {
377+
return () => h('button', { onClick: () => emit('click') }, 'click me')
378+
},
379+
})
380+
381+
const fn = vi.fn()
382+
const VaporChild = defineVaporComponent({
383+
emits: ['click'],
384+
setup() {
385+
return createComponent(
386+
VDomChild as any,
387+
{ onClick: () => fn },
388+
null,
389+
true,
390+
)
391+
},
392+
})
393+
394+
const App = {
395+
setup() {
396+
return () => h(VaporChild as any)
397+
},
398+
}
399+
400+
const root = document.createElement('div')
401+
createApp(App).use(vaporInteropPlugin).mount(root)
402+
403+
expect(root.innerHTML).toBe('<button>click me</button>')
404+
const button = root.querySelector('button')!
405+
button.dispatchEvent(new Event('click'))
406+
407+
// fn should be called once
408+
expect(fn).toHaveBeenCalledTimes(1)
409+
})
364410
})

packages/runtime-vapor/src/vdomInterop.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,11 @@ import {
99
type Slots,
1010
type VNode,
1111
type VaporInteropInterface,
12+
createInternalObject,
1213
createVNode,
1314
currentInstance,
1415
ensureRenderer,
16+
isEmitListener,
1517
onScopeDispose,
1618
renderSlot,
1719
shallowRef,
@@ -162,7 +164,14 @@ function createVDOMComponent(
162164
// overwrite how the vdom instance handles props
163165
vnode.vi = (instance: ComponentInternalInstance) => {
164166
instance.props = wrapper.props
165-
instance.attrs = wrapper.attrs
167+
168+
const attrs = (instance.attrs = createInternalObject())
169+
for (const key in wrapper.attrs) {
170+
if (!isEmitListener(instance.emitsOptions, key)) {
171+
attrs[key] = wrapper.attrs[key]
172+
}
173+
}
174+
166175
instance.slots =
167176
wrapper.slots === EMPTY_OBJ
168177
? EMPTY_OBJ

0 commit comments

Comments
 (0)