]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(runtime-vapor): component emits vdom interop (#13498)
authoredison <daiwei521@126.com>
Wed, 16 Jul 2025 05:49:52 +0000 (13:49 +0800)
committerGitHub <noreply@github.com>
Wed, 16 Jul 2025 05:49:52 +0000 (13:49 +0800)
packages/runtime-vapor/__tests__/vdomInterop.spec.ts
packages/runtime-vapor/src/componentEmits.ts
packages/runtime-vapor/src/componentProps.ts
packages/runtime-vapor/src/vdomInterop.ts

index dd51652550e842ef6c9a5cfd75b764a829492af3..96222e6ee0b5fbc5be10cf1bf901a25b46ead036 100644 (file)
@@ -26,7 +26,27 @@ describe('vdomInterop', () => {
     })
   })
 
-  describe.todo('emit', () => {})
+  describe('emit', () => {
+    test('emit from vapor child to vdom parent', () => {
+      const VaporChild = defineVaporComponent({
+        emits: ['click'],
+        setup(_, { emit }) {
+          emit('click')
+          return []
+        },
+      })
+
+      const fn = vi.fn()
+      define({
+        setup() {
+          return () => h(VaporChild as any, { onClick: fn })
+        },
+      }).render()
+
+      // fn should be called once
+      expect(fn).toHaveBeenCalledTimes(1)
+    })
+  })
 
   describe('slots', () => {
     test('basic', () => {
index 68b7cfbeb21cf6e40d90a1a19d06718296629565..daee4d59fe7241c8f81b7e33cd6053a142282c27 100644 (file)
@@ -1,7 +1,8 @@
 import { type ObjectEmitsOptions, baseEmit } from '@vue/runtime-dom'
 import type { VaporComponent, VaporComponentInstance } from './component'
 import { EMPTY_OBJ, hasOwn, isArray } from '@vue/shared'
-import { resolveSource } from './componentProps'
+import { type RawProps, resolveSource } from './componentProps'
+import { interopKey } from './vdomInterop'
 
 /**
  * The logic from core isn't too reusable so it's better to duplicate here
@@ -40,13 +41,17 @@ export function emit(
   )
 }
 
-function propGetter(rawProps: Record<string, any>, key: string) {
+function propGetter(rawProps: RawProps, key: string) {
   const dynamicSources = rawProps.$
   if (dynamicSources) {
     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[interopKey]
+          ? source[key]
+          : resolveSource(source[key])
     }
   }
   return rawProps[key] && resolveSource(rawProps[key])
index b870a21b2e8e0eaec4cbee9ec91d57358d97085e..55eadb980468083789a5871efeee7e20220d2dc0 100644 (file)
@@ -23,10 +23,11 @@ import {
 import { ReactiveFlags } from '@vue/reactivity'
 import { normalizeEmitsOptions } from './componentEmits'
 import { renderEffect } from './renderEffect'
+import type { interopKey } from './vdomInterop'
 
 export type RawProps = Record<string, () => unknown> & {
   // generated by compiler for :[key]="x" or v-bind="x"
-  $?: DynamicPropsSource[]
+  $?: DynamicPropsSource[] & { [interopKey]?: boolean }
 }
 
 export type DynamicPropsSource =
index 8c1dd2cee2ba4c2ee2beddcc64025ed5909da542..1573a306922aacd6813fc8cdfe8f7c7a84ec4f4a 100644 (file)
@@ -37,6 +37,8 @@ import { renderEffect } from './renderEffect'
 import { createTextNode } from './dom/node'
 import { optimizePropertyLookup } from './dom/prop'
 
+export const interopKey: unique symbol = Symbol(`interop`)
+
 // mounting vapor components and slots in vdom
 const vaporInteropImpl: Omit<
   VaporInteropInterface,
@@ -51,11 +53,16 @@ const vaporInteropImpl: Omit<
     const propsRef = shallowRef(vnode.props)
     const slotsRef = shallowRef(vnode.children)
 
+    const dynamicPropSource: (() => any)[] & { [interopKey]?: boolean } = [
+      () => propsRef.value,
+    ]
+    // mark as interop props
+    dynamicPropSource[interopKey] = true
     // @ts-expect-error
     const instance = (vnode.component = createComponent(
       vnode.type as any as VaporComponent,
       {
-        $: [() => propsRef.value],
+        $: dynamicPropSource,
       } as RawProps,
       {
         _: slotsRef, // pass the slots ref