]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(runtime-vapor): dynamic component attrs fallthrough 13466/head
authordaiwei <daiwei521@126.com>
Thu, 12 Jun 2025 13:43:12 +0000 (21:43 +0800)
committerdaiwei <daiwei521@126.com>
Thu, 12 Jun 2025 13:43:12 +0000 (21:43 +0800)
packages/runtime-vapor/__tests__/componentAttrs.spec.ts
packages/runtime-vapor/src/component.ts
packages/runtime-vapor/src/componentProps.ts

index e4076855cb40759392b0aa294f2ca17d336788b3..e95c683abe493ba3648460eb6a3deced71ca3eae 100644 (file)
@@ -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(
+      '<button class="foo"><!--slot--></button><!--dynamic-component-->',
+    )
+  })
+
+  it('parent value should take priority', async () => {
     const parentVal = ref('parent')
     const childVal = ref('child')
 
index 548babebf8beef2115e31356d50a989e2e1a0112..55edf38c6b4e29df971f4b0da5df6733f4848eed 100644 (file)
@@ -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
+    }
+  }
+}
index a5e9daad229ca25c684614de03d195ebc2aee985..7a0e9ed9286a029c992ab43fc3deb06d88e4ef7d 100644 (file)
@@ -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
       }
     }