]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(custom-element): use PatchFlags.BAIL for slot when props are present (#13907)
authoredison <daiwei521@126.com>
Wed, 24 Sep 2025 09:08:25 +0000 (17:08 +0800)
committerGitHub <noreply@github.com>
Wed, 24 Sep 2025 09:08:25 +0000 (17:08 +0800)
close #13904

packages/runtime-core/src/helpers/renderSlot.ts
packages/runtime-dom/__tests__/customElement.spec.ts

index 92f7dab36b621f2be3cf2b7a49ea2e9b066f6123..2f1296bb13e9310392a68e627e0f37d6715c43e4 100644 (file)
@@ -37,6 +37,7 @@ export function renderSlot(
       isAsyncWrapper(currentRenderingInstance!.parent) &&
       currentRenderingInstance!.parent.ce)
   ) {
+    const hasProps = Object.keys(props).length > 0
     // in custom element mode, render <slot/> as actual slot outlets
     // wrap it with a fragment because in shadowRoot: false mode the slot
     // element gets replaced by injected content
@@ -47,7 +48,7 @@ export function renderSlot(
         Fragment,
         null,
         [createVNode('slot', props, fallback && fallback())],
-        PatchFlags.STABLE_FRAGMENT,
+        hasProps ? PatchFlags.BAIL : PatchFlags.STABLE_FRAGMENT,
       )
     )
   }
index cb09cf4d9e7d487c869a12861e2bea2a5949af5d..52350dfd27ac893a566d0addca31eb3bf752fc11 100644 (file)
@@ -638,6 +638,33 @@ describe('defineCustomElement', () => {
         `<div><slot><div>fallback</div></slot></div><div><slot name="named"></slot></div>`,
       )
     })
+
+    test('render slot props', async () => {
+      const foo = ref('foo')
+      const E = defineCustomElement({
+        render() {
+          return [
+            h(
+              'div',
+              null,
+              renderSlot(this.$slots, 'default', { class: foo.value }),
+            ),
+          ]
+        },
+      })
+      customElements.define('my-el-slot-props', E)
+      container.innerHTML = `<my-el-slot-props><span>hi</span></my-el-slot-props>`
+      const e = container.childNodes[0] as VueElement
+      expect(e.shadowRoot!.innerHTML).toBe(
+        `<div><slot class="foo"></slot></div>`,
+      )
+
+      foo.value = 'bar'
+      await nextTick()
+      expect(e.shadowRoot!.innerHTML).toBe(
+        `<div><slot class="bar"></slot></div>`,
+      )
+    })
   })
 
   describe('provide/inject', () => {