fix(runtime-vapor): should not fallthrough emit handlers to vdom child

This commit is contained in:
daiwei 2025-06-19 17:46:51 +08:00
parent 88ef97ffd3
commit c59a9406e8
3 changed files with 61 additions and 2 deletions

View File

@ -557,3 +557,7 @@ export { startMeasure, endMeasure } from './profiling'
* @internal
*/
export { initFeatureFlags } from './featureFlags'
/**
* @internal
*/
export { createInternalObject } from './internalObject'

View File

@ -1,4 +1,11 @@
import { type Ref, nextTick, ref } from '@vue/runtime-dom'
import {
type Ref,
createApp,
defineComponent,
h,
nextTick,
ref,
} from '@vue/runtime-dom'
import {
createComponent,
defineVaporComponent,
@ -8,6 +15,7 @@ import {
setProp,
setStyle,
template,
vaporInteropPlugin,
} from '../src'
import { makeRender } from './_utils'
import { stringifyStyle } from '@vue/shared'
@ -361,4 +369,42 @@ describe('attribute fallthrough', () => {
const el = host.children[0]
expect(el.classList.length).toBe(0)
})
it('should not fallthrough emit handlers to vdom child', () => {
const VDomChild = defineComponent({
emits: ['click'],
setup(_, { emit }) {
return () => h('button', { onClick: () => emit('click') }, 'click me')
},
})
const fn = vi.fn()
const VaporChild = defineVaporComponent({
emits: ['click'],
setup() {
return createComponent(
VDomChild as any,
{ onClick: () => fn },
null,
true,
)
},
})
const App = {
setup() {
return () => h(VaporChild as any)
},
}
const root = document.createElement('div')
createApp(App).use(vaporInteropPlugin).mount(root)
expect(root.innerHTML).toBe('<button>click me</button>')
const button = root.querySelector('button')!
button.dispatchEvent(new Event('click'))
// fn should be called once
expect(fn).toHaveBeenCalledTimes(1)
})
})

View File

@ -9,9 +9,11 @@ import {
type Slots,
type VNode,
type VaporInteropInterface,
createInternalObject,
createVNode,
currentInstance,
ensureRenderer,
isEmitListener,
onScopeDispose,
renderSlot,
shallowRef,
@ -162,7 +164,14 @@ function createVDOMComponent(
// overwrite how the vdom instance handles props
vnode.vi = (instance: ComponentInternalInstance) => {
instance.props = wrapper.props
instance.attrs = wrapper.attrs
const attrs = (instance.attrs = createInternalObject())
for (const key in wrapper.attrs) {
if (!isEmitListener(instance.emitsOptions, key)) {
attrs[key] = wrapper.attrs[key]
}
}
instance.slots =
wrapper.slots === EMPTY_OBJ
? EMPTY_OBJ