]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(runtime-vapor): dynamic component attrs fallthrough (#13466)
authoredison <daiwei521@126.com>
Fri, 20 Jun 2025 00:40:52 +0000 (08:40 +0800)
committerGitHub <noreply@github.com>
Fri, 20 Jun 2025 00:40:52 +0000 (08:40 +0800)
packages/runtime-vapor/__tests__/componentAttrs.spec.ts
packages/runtime-vapor/src/component.ts
packages/runtime-vapor/src/componentProps.ts

index 204dd599011fa310e7612531cdb1410fbe8d0a49..109b1aa2fa4b96ffe959e606d78216631e535581 100644 (file)
@@ -8,6 +8,8 @@ import {
 } from '@vue/runtime-dom'
 import {
   createComponent,
+  createDynamicComponent,
+  createSlot,
   defineVaporComponent,
   renderEffect,
   setClass,
@@ -285,7 +287,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 deec96cb0a27a094ffcca10341ae5d2b1bbd92b0..af15133dbe54f7510deb7abc2bf85e88f76f6250 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,
@@ -255,14 +255,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()
@@ -563,3 +565,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
       }
     }