]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(custom-element): ensure proper remount and prevent redundant slot parsing with...
authoredison <daiwei521@126.com>
Thu, 22 May 2025 00:05:39 +0000 (08:05 +0800)
committerGitHub <noreply@github.com>
Thu, 22 May 2025 00:05:39 +0000 (08:05 +0800)
close #13199

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

index 907b299b824fdc25b296a33d0c9f32642d71d052..cf7b94ed00854e4c61f57e4c382fe47bb4f50123 100644 (file)
@@ -1226,6 +1226,92 @@ describe('defineCustomElement', () => {
       expect(target.innerHTML).toBe(`<span>default</span>`)
       app.unmount()
     })
+
+    test('toggle nested custom element with shadowRoot: false', async () => {
+      customElements.define(
+        'my-el-child-shadow-false',
+        defineCustomElement(
+          {
+            render(ctx: any) {
+              return h('div', null, [renderSlot(ctx.$slots, 'default')])
+            },
+          },
+          { shadowRoot: false },
+        ),
+      )
+      const ChildWrapper = {
+        render() {
+          return h('my-el-child-shadow-false', null, 'child')
+        },
+      }
+
+      customElements.define(
+        'my-el-parent-shadow-false',
+        defineCustomElement(
+          {
+            props: {
+              isShown: { type: Boolean, required: true },
+            },
+            render(ctx: any, _: any, $props: any) {
+              return $props.isShown
+                ? h('div', { key: 0 }, [renderSlot(ctx.$slots, 'default')])
+                : null
+            },
+          },
+          { shadowRoot: false },
+        ),
+      )
+      const ParentWrapper = {
+        props: {
+          isShown: { type: Boolean, required: true },
+        },
+        render(ctx: any, _: any, $props: any) {
+          return h('my-el-parent-shadow-false', { isShown: $props.isShown }, [
+            renderSlot(ctx.$slots, 'default'),
+          ])
+        },
+      }
+
+      const isShown = ref(true)
+      const App = {
+        render() {
+          return h(ParentWrapper, { isShown: isShown.value } as any, {
+            default: () => [h(ChildWrapper)],
+          })
+        },
+      }
+      const container = document.createElement('div')
+      document.body.appendChild(container)
+      const app = createApp(App)
+      app.mount(container)
+      expect(container.innerHTML).toBe(
+        `<my-el-parent-shadow-false is-shown="" data-v-app="">` +
+          `<div>` +
+          `<my-el-child-shadow-false data-v-app="">` +
+          `<div>child</div>` +
+          `</my-el-child-shadow-false>` +
+          `</div>` +
+          `</my-el-parent-shadow-false>`,
+      )
+
+      isShown.value = false
+      await nextTick()
+      expect(container.innerHTML).toBe(
+        `<my-el-parent-shadow-false data-v-app=""><!----></my-el-parent-shadow-false>`,
+      )
+
+      isShown.value = true
+      await nextTick()
+      expect(container.innerHTML).toBe(
+        `<my-el-parent-shadow-false data-v-app="" is-shown="">` +
+          `<div>` +
+          `<my-el-child-shadow-false data-v-app="">` +
+          `<div>child</div>` +
+          `</my-el-child-shadow-false>` +
+          `</div>` +
+          `</my-el-parent-shadow-false>`,
+      )
+    })
   })
 
   describe('helpers', () => {
index f07246630904495752e6eb0beaa644b286722246..3a4a34129794c1f05a30ffbfe3449601032d49e7 100644 (file)
@@ -280,7 +280,8 @@ export class VueElement
     // avoid resolving component if it's not connected
     if (!this.isConnected) return
 
-    if (!this.shadowRoot) {
+    // avoid re-parsing slots if already resolved
+    if (!this.shadowRoot && !this._resolved) {
       this._parseSlots()
     }
     this._connected = true
@@ -298,8 +299,7 @@ export class VueElement
 
     if (!this._instance) {
       if (this._resolved) {
-        this._setParent()
-        this._update()
+        this._mount(this._def)
       } else {
         if (parent && parent._pendingResolve) {
           this._pendingResolve = parent._pendingResolve.then(() => {