From 26dbd3c902cb34f064b9af726ea66b1b9eefe779 Mon Sep 17 00:00:00 2001 From: daiwei Date: Thu, 19 Jun 2025 16:52:37 +0800 Subject: [PATCH] fix(vapor): component emits vdom interop --- .../__tests__/componentEmits.spec.ts | 33 ++++++++++++++++++- packages/runtime-vapor/src/componentEmits.ts | 6 +++- packages/runtime-vapor/src/componentProps.ts | 2 +- packages/runtime-vapor/src/vdomInterop.ts | 2 +- 4 files changed, 39 insertions(+), 4 deletions(-) diff --git a/packages/runtime-vapor/__tests__/componentEmits.spec.ts b/packages/runtime-vapor/__tests__/componentEmits.spec.ts index 8c8a56085..b07125bc4 100644 --- a/packages/runtime-vapor/__tests__/componentEmits.spec.ts +++ b/packages/runtime-vapor/__tests__/componentEmits.spec.ts @@ -4,12 +4,18 @@ // ./rendererAttrsFallthrough.spec.ts. import { + createApp, + h, isEmitListener, nextTick, onBeforeUnmount, toHandlers, } from '@vue/runtime-dom' -import { createComponent, defineVaporComponent } from '../src' +import { + createComponent, + defineVaporComponent, + vaporInteropPlugin, +} from '../src' import { makeRender } from './_utils' const define = makeRender() @@ -425,3 +431,28 @@ describe('component: emit', () => { expect(fn).not.toHaveBeenCalled() }) }) + +describe('vdom interop', () => { + test('vdom parent > vapor child', () => { + const VaporChild = defineVaporComponent({ + emits: ['click'], + setup(_, { emit }) { + emit('click') + return [] + }, + }) + + const fn = vi.fn() + const App = { + setup() { + return () => h(VaporChild as any, { onClick: fn }) + }, + } + + const root = document.createElement('div') + createApp(App).use(vaporInteropPlugin).mount(root) + + // fn should be called once + expect(fn).toHaveBeenCalledTimes(1) + }) +}) diff --git a/packages/runtime-vapor/src/componentEmits.ts b/packages/runtime-vapor/src/componentEmits.ts index 68b7cfbeb..beec548b3 100644 --- a/packages/runtime-vapor/src/componentEmits.ts +++ b/packages/runtime-vapor/src/componentEmits.ts @@ -46,7 +46,11 @@ function propGetter(rawProps: Record, key: string) { let i = dynamicSources.length while (i--) { const source = resolveSource(dynamicSources[i]) - if (hasOwn(source, key)) return resolveSource(source[key]) + if (hasOwn(source, key)) + // for props passed from VDOM component, no need to resolve + return dynamicSources.__interop + ? source[key] + : resolveSource(source[key]) } } return rawProps[key] && resolveSource(rawProps[key]) diff --git a/packages/runtime-vapor/src/componentProps.ts b/packages/runtime-vapor/src/componentProps.ts index a5e9daad2..b088b68c1 100644 --- a/packages/runtime-vapor/src/componentProps.ts +++ b/packages/runtime-vapor/src/componentProps.ts @@ -26,7 +26,7 @@ import { renderEffect } from './renderEffect' export type RawProps = Record unknown> & { // generated by compiler for :[key]="x" or v-bind="x" - $?: DynamicPropsSource[] + $?: DynamicPropsSource[] & { __interop?: boolean } } export type DynamicPropsSource = diff --git a/packages/runtime-vapor/src/vdomInterop.ts b/packages/runtime-vapor/src/vdomInterop.ts index 77228fd72..0d2290cc0 100644 --- a/packages/runtime-vapor/src/vdomInterop.ts +++ b/packages/runtime-vapor/src/vdomInterop.ts @@ -52,7 +52,7 @@ const vaporInteropImpl: Omit< const instance = (vnode.component = createComponent( vnode.type as any as VaporComponent, { - $: [() => propsRef.value], + $: extend([() => propsRef.value], { __interop: true }), } as RawProps, { _: slotsRef, // pass the slots ref