From 305a0e8b8956348c8b57747b930cf0a8855af639 Mon Sep 17 00:00:00 2001 From: daiwei Date: Tue, 8 Apr 2025 14:52:33 +0800 Subject: [PATCH] fix(templateRef): add support for dynamic components --- .../__tests__/dom/templateRef.spec.ts | 39 ++++++++++++++++++- packages/runtime-vapor/src/apiTemplateRef.ts | 12 +++++- 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/packages/runtime-vapor/__tests__/dom/templateRef.spec.ts b/packages/runtime-vapor/__tests__/dom/templateRef.spec.ts index e696fadd4..7e6d140b6 100644 --- a/packages/runtime-vapor/__tests__/dom/templateRef.spec.ts +++ b/packages/runtime-vapor/__tests__/dom/templateRef.spec.ts @@ -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(`
`)() as any + const x0 = child(n0) as any + renderEffect(() => setText(x0, msg.value)) + return n0 + }, + }) + + const views: Record = { child: Child } + const view = ref('child') + const refKey = ref>(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('
one
') + expect(refKey.value).toBeDefined() + + refKey.value.setMsg('changed') + await nextTick() + expect(html()).toBe('
changed
') + }) + // TODO: can not reproduce in Vapor // // #2078 // test('handling multiple merged refs', async () => { diff --git a/packages/runtime-vapor/src/apiTemplateRef.ts b/packages/runtime-vapor/src/apiTemplateRef.ts index c5a6c5fb2..d3f3cf718 100644 --- a/packages/runtime-vapor/src/apiTemplateRef.ts +++ b/packages/runtime-vapor/src/apiTemplateRef.ts @@ -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 +}