]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(custom-element): optimize slot retrieval to avoid duplicates (#13961)
authoredison <daiwei521@126.com>
Wed, 5 Nov 2025 09:04:33 +0000 (17:04 +0800)
committerGitHub <noreply@github.com>
Wed, 5 Nov 2025 09:04:33 +0000 (17:04 +0800)
close #13955

packages/runtime-dom/__tests__/customElement.spec.ts
packages/runtime-dom/src/apiCustomElement.ts

index 04953f8251c32864e22d2b266c49260a0fe96238..1225326abc50519d7b9a30f15d40a83ad143f151 100644 (file)
@@ -1614,6 +1614,44 @@ describe('defineCustomElement', () => {
       app.unmount()
     })
 
+    test('teleport target is ancestor of custom element host', async () => {
+      const Child = defineCustomElement(
+        {
+          render() {
+            return [
+              h(Teleport, { to: '#t1' }, [renderSlot(this.$slots, 'header')]),
+            ]
+          },
+        },
+        { shadowRoot: false },
+      )
+      customElements.define('my-el-teleport-child-target', Child)
+
+      const App = {
+        render() {
+          return h('div', { id: 't1' }, [
+            h('my-el-teleport-child-target', null, {
+              default: () => [h('div', { slot: 'header' }, 'header')],
+            }),
+          ])
+        },
+      }
+      const app = createApp(App)
+      app.mount(container)
+
+      const target1 = document.getElementById('t1')!
+      expect(target1.outerHTML).toBe(
+        `<div id="t1">` +
+          `<my-el-teleport-child-target data-v-app="">` +
+          `<!--teleport start--><!--teleport end-->` +
+          `</my-el-teleport-child-target>` +
+          `<div slot="header">header</div>` +
+          `</div>`,
+      )
+
+      app.unmount()
+    })
+
     test('toggle nested custom element with shadowRoot: false', async () => {
       customElements.define(
         'my-el-child-shadow-false',
index a8f64210c3ca30a7d0acd4475c9391fdaa0ec2d8..85d37bc117e06f337dff6d6451e5b81b7479d558 100644 (file)
@@ -688,11 +688,18 @@ export class VueElement
     if (this._teleportTargets) {
       roots.push(...this._teleportTargets)
     }
-    return roots.reduce<HTMLSlotElement[]>((res, i) => {
-      res.push(...Array.from(i.querySelectorAll('slot')))
-      return res
-    }, [])
+
+    const slots = new Set<HTMLSlotElement>()
+    for (const root of roots) {
+      const found = root.querySelectorAll<HTMLSlotElement>('slot')
+      for (let i = 0; i < found.length; i++) {
+        slots.add(found[i])
+      }
+    }
+
+    return Array.from(slots)
   }
+
   /**
    * @internal
    */