From a93b49ce975e6b969e9b0633cc6cb14009ba89e7 Mon Sep 17 00:00:00 2001 From: daiwei Date: Thu, 12 Jun 2025 21:43:12 +0800 Subject: [PATCH] fix(runtime-vapor): dynamic component attrs fallthrough --- .../__tests__/componentAttrs.spec.ts | 40 ++++++++++++++++++- packages/runtime-vapor/src/component.ts | 31 ++++++++++---- packages/runtime-vapor/src/componentProps.ts | 3 +- 3 files changed, 65 insertions(+), 9 deletions(-) diff --git a/packages/runtime-vapor/__tests__/componentAttrs.spec.ts b/packages/runtime-vapor/__tests__/componentAttrs.spec.ts index e4076855cb4..e95c683abe4 100644 --- a/packages/runtime-vapor/__tests__/componentAttrs.spec.ts +++ b/packages/runtime-vapor/__tests__/componentAttrs.spec.ts @@ -1,6 +1,8 @@ import { type Ref, nextTick, ref } from '@vue/runtime-dom' import { createComponent, + createDynamicComponent, + createSlot, defineVaporComponent, renderEffect, setClass, @@ -277,7 +279,43 @@ describe('attribute fallthrough', () => { expect(getCSS()).not.toContain('font-size:bold') }) - test('parent value should take priority', async () => { + it('should fallthrough attrs to dynamic component', async () => { + const Comp = defineVaporComponent({ + setup() { + const n1 = createDynamicComponent( + () => 'button', + null, + { + default: () => { + const n0 = createSlot('default', null) + return n0 + }, + }, + true, + ) + return n1 + }, + }) + + const { html } = define({ + setup() { + return createComponent( + Comp, + { + class: () => 'foo', + }, + null, + true, + ) + }, + }).render() + + expect(html()).toBe( + '', + ) + }) + + it('parent value should take priority', async () => { const parentVal = ref('parent') const childVal = ref('child') diff --git a/packages/runtime-vapor/src/component.ts b/packages/runtime-vapor/src/component.ts index 548babebf8b..55edf38c6b4 100644 --- a/packages/runtime-vapor/src/component.ts +++ b/packages/runtime-vapor/src/component.ts @@ -25,7 +25,7 @@ import { unregisterHMR, warn, } from '@vue/runtime-dom' -import { type Block, insert, isBlock, remove } from './block' +import { type Block, DynamicFragment, insert, isBlock, remove } from './block' import { type ShallowRef, markRaw, @@ -249,14 +249,16 @@ export function createComponent( if ( instance.hasFallthrough && component.inheritAttrs !== false && - instance.block instanceof Element && Object.keys(instance.attrs).length ) { - renderEffect(() => { - isApplyingFallthroughProps = true - setDynamicProps(instance.block as Element, [instance.attrs]) - isApplyingFallthroughProps = false - }) + const el = getRootElement(instance) + if (el) { + renderEffect(() => { + isApplyingFallthroughProps = true + setDynamicProps(el, [instance.attrs]) + isApplyingFallthroughProps = false + }) + } } resetTracking() @@ -545,3 +547,18 @@ export function getExposed( ) } } + +function getRootElement({ + block, +}: VaporComponentInstance): Element | undefined { + if (block instanceof Element) { + return block + } + + if (block instanceof DynamicFragment) { + const { nodes } = block + if (nodes instanceof Element && (nodes as any).$root) { + return nodes + } + } +} diff --git a/packages/runtime-vapor/src/componentProps.ts b/packages/runtime-vapor/src/componentProps.ts index a5e9daad229..7a0e9ed9286 100644 --- a/packages/runtime-vapor/src/componentProps.ts +++ b/packages/runtime-vapor/src/componentProps.ts @@ -210,7 +210,8 @@ export function hasAttrFromRawProps(rawProps: RawProps, key: string): boolean { if (dynamicSources) { let i = dynamicSources.length while (i--) { - if (hasOwn(resolveSource(dynamicSources[i]), key)) { + const source = resolveSource(dynamicSources[i]) + if (source && hasOwn(source, key)) { return true } }