]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(vapor): component emits vdom interop
authordaiwei <daiwei521@126.com>
Thu, 19 Jun 2025 08:52:37 +0000 (16:52 +0800)
committerdaiwei <daiwei521@126.com>
Fri, 20 Jun 2025 01:20:07 +0000 (09:20 +0800)
packages/runtime-vapor/__tests__/componentEmits.spec.ts
packages/runtime-vapor/src/componentEmits.ts
packages/runtime-vapor/src/componentProps.ts
packages/runtime-vapor/src/vdomInterop.ts

index 8c8a56085bafbc7eb0a11fd688f345cd6b442416..b07125bc4a7db40841aba79a608aa210d3a1aba0 100644 (file)
@@ -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)
+  })
+})
index 68b7cfbeb21cf6e40d90a1a19d06718296629565..beec548b39a576f8167e70b43bf0ac23281f1b5c 100644 (file)
@@ -46,7 +46,11 @@ function propGetter(rawProps: Record<string, any>, 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])
index a5e9daad229ca25c684614de03d195ebc2aee985..b088b68c1c0a48a98f6b74a910ab925fc6e5a712 100644 (file)
@@ -26,7 +26,7 @@ import { renderEffect } from './renderEffect'
 
 export type RawProps = Record<string, () => unknown> & {
   // generated by compiler for :[key]="x" or v-bind="x"
-  $?: DynamicPropsSource[]
+  $?: DynamicPropsSource[] & { __interop?: boolean }
 }
 
 export type DynamicPropsSource =
index 77228fd72a02fe85a5496daf7d89bc37e197a4d2..0d2290cc062efa6e1201ac6786c1f8216275f4f5 100644 (file)
@@ -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