diff --git a/packages/runtime-core/src/index.ts b/packages/runtime-core/src/index.ts index e309554f2..1ed6f21df 100644 --- a/packages/runtime-core/src/index.ts +++ b/packages/runtime-core/src/index.ts @@ -557,3 +557,7 @@ export { startMeasure, endMeasure } from './profiling' * @internal */ export { initFeatureFlags } from './featureFlags' +/** + * @internal + */ +export { createInternalObject } from './internalObject' diff --git a/packages/runtime-vapor/__tests__/componentAttrs.spec.ts b/packages/runtime-vapor/__tests__/componentAttrs.spec.ts index fc6fae5f0..204dd5990 100644 --- a/packages/runtime-vapor/__tests__/componentAttrs.spec.ts +++ b/packages/runtime-vapor/__tests__/componentAttrs.spec.ts @@ -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('') + const button = root.querySelector('button')! + button.dispatchEvent(new Event('click')) + + // fn should be called once + expect(fn).toHaveBeenCalledTimes(1) + }) }) diff --git a/packages/runtime-vapor/src/vdomInterop.ts b/packages/runtime-vapor/src/vdomInterop.ts index 77228fd72..b916a2c8e 100644 --- a/packages/runtime-vapor/src/vdomInterop.ts +++ b/packages/runtime-vapor/src/vdomInterop.ts @@ -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