]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(runtime-vapor): set ref on dynamic component (#13172)
authoredison <daiwei521@126.com>
Wed, 18 Jun 2025 00:51:29 +0000 (08:51 +0800)
committerGitHub <noreply@github.com>
Wed, 18 Jun 2025 00:51:29 +0000 (08:51 +0800)
packages/runtime-vapor/__tests__/dom/templateRef.spec.ts
packages/runtime-vapor/src/apiTemplateRef.ts

index e696fadd469cda2971f0e9d025202f6ece648bab..f1ce23ac156b5ceddd4238063c14245c8057892e 100644 (file)
@@ -1,10 +1,13 @@
 import type { NodeRef } from '../../src/apiTemplateRef'
 import {
+  child,
   createComponent,
+  createDynamicComponent,
   createFor,
   createIf,
   createSlot,
   createTemplateRefSetter,
+  defineVaporComponent,
   insert,
   renderEffect,
   template,
@@ -19,7 +22,8 @@ import {
   useTemplateRef,
   watchEffect,
 } from '@vue/runtime-dom'
-import { setElementText } from '../../src/dom/prop'
+import { setElementText, setText } from '../../src/dom/prop'
+import type { VaporComponent } from '../../src/component'
 
 const define = makeRender()
 
@@ -676,6 +680,39 @@ describe('api: template ref', () => {
     expect(r!.value).toBe(n)
   })
 
+  test('work with dynamic component', async () => {
+    const Child = defineVaporComponent({
+      setup(_, { expose }) {
+        const msg = ref('one')
+        expose({ setMsg: (m: string) => (msg.value = m) })
+        const n0 = template(`<div> </div>`)() as any
+        const x0 = child(n0) as any
+        renderEffect(() => setText(x0, msg.value))
+        return n0
+      },
+    })
+
+    const views: Record<string, VaporComponent> = { child: Child }
+    const view = ref('child')
+    const refKey = ref<any>(null)
+
+    const { html } = define({
+      setup() {
+        const setRef = createTemplateRefSetter()
+        const n0 = createDynamicComponent(() => views[view.value]) as any
+        setRef(n0, refKey)
+        return n0
+      },
+    }).render()
+
+    expect(html()).toBe('<div>one</div><!--dynamic-component-->')
+    expect(refKey.value).toBeDefined()
+
+    refKey.value.setMsg('changed')
+    await nextTick()
+    expect(html()).toBe('<div>changed</div><!--dynamic-component-->')
+  })
+
   // TODO: can not reproduce in Vapor
   // // #2078
   // test('handling multiple merged refs', async () => {
index c5a6c5fb2b67b8b50e855686f3f6dd61c8a60338..d3f3cf71819622c7b7ec988d548f5475ee2c754b 100644 (file)
@@ -20,6 +20,7 @@ import {
   isString,
   remove,
 } from '@vue/shared'
+import { DynamicFragment } from './block'
 
 export type NodeRef = string | Ref | ((ref: Element) => void)
 export type RefEl = Element | VaporComponentInstance
@@ -49,7 +50,7 @@ export function setRef(
   if (!instance || instance.isUnmounted) return
 
   const setupState: any = __DEV__ ? instance.setupState || {} : null
-  const refValue = isVaporComponent(el) ? getExposed(el) || el : el
+  const refValue = getRefValue(el)
 
   const refs =
     instance.refs === EMPTY_OBJ ? (instance.refs = {}) : instance.refs
@@ -143,3 +144,12 @@ export function setRef(
   }
   return ref
 }
+
+const getRefValue = (el: RefEl) => {
+  if (isVaporComponent(el)) {
+    return getExposed(el) || el
+  } else if (el instanceof DynamicFragment) {
+    return getRefValue(el.nodes as RefEl)
+  }
+  return el
+}