diff --git a/packages/runtime-dom/__tests__/customElement.spec.ts b/packages/runtime-dom/__tests__/customElement.spec.ts
index 976213f3783..c0474768266 100644
--- a/packages/runtime-dom/__tests__/customElement.spec.ts
+++ b/packages/runtime-dom/__tests__/customElement.spec.ts
@@ -147,10 +147,12 @@ describe('defineCustomElement', () => {
})
// #12412
+ const contextElStyle = ':host { color: red }'
const ContextEl = defineCustomElement({
props: {
msg: String,
},
+ styles: [contextElStyle],
setup(props, { expose }) {
expose({
text: () => props.msg,
@@ -171,27 +173,33 @@ describe('defineCustomElement', () => {
await nextTick()
await nextTick() // wait two ticks for disconnect
expect('text' in parent).toBe(false)
+ expect(child.shadowRoot!.querySelectorAll('style').length).toBe(1)
container.appendChild(parent) // should not throw Error
await nextTick()
expect(parent.text()).toBe('msg1')
- expect(parent.shadowRoot!.textContent).toBe('msg1')
- expect(child.shadowRoot!.textContent).toBe('msg1')
+ expect(parent.shadowRoot!.textContent).toBe(contextElStyle + 'msg1')
+ expect(child.shadowRoot!.textContent).toBe(contextElStyle + 'msg1')
parent.setAttribute('msg', 'msg2')
await nextTick()
- expect(parent.shadowRoot!.textContent).toBe('msg2')
+ expect(parent.shadowRoot!.textContent).toBe(contextElStyle + 'msg2')
await nextTick()
- expect(child.shadowRoot!.textContent).toBe('msg2')
+ expect(child.shadowRoot!.textContent).toBe(contextElStyle + 'msg2')
+ expect(child.shadowRoot!.querySelectorAll('style').length).toBe(1)
})
- test('move element to change parent and context', async () => {
+ test('move element to new parent', async () => {
container.innerHTML = ``
const first = container.children[0] as VueElement,
- second = container.children[1] as VueElement
+ second = container.children[1] as VueElement & { text: () => string }
await nextTick()
- expect(second.shadowRoot!.textContent).toBe('msg2')
+ expect(second.shadowRoot!.textContent).toBe(contextElStyle + 'msg2')
first.append(second)
await nextTick()
- expect(second.shadowRoot!.textContent).toBe('msg1')
+ expect(second.shadowRoot!.textContent).toBe(contextElStyle + 'msg1')
+ expect(second.shadowRoot!.querySelectorAll('style').length).toBe(1)
+ second.setAttribute('msg', 'msg3')
+ await nextTick()
+ expect(second.text()).toBe('msg3')
})
})
diff --git a/packages/runtime-dom/src/apiCustomElement.ts b/packages/runtime-dom/src/apiCustomElement.ts
index 0e72fa0adb5..b2b2d3f2011 100644
--- a/packages/runtime-dom/src/apiCustomElement.ts
+++ b/packages/runtime-dom/src/apiCustomElement.ts
@@ -298,12 +298,13 @@ export class VueElement
}
}
- // unmount if parent changed and previously mounted, should keep parent
+ // unmount if parent changed and previously mounted, should keep parent and observer
if (this._instance && parentChanged) this._unmount(true)
- if (!this._instance) {
+ if (!this._instance || parentChanged) {
if (this._resolved) {
- this._setParent()
- this._update()
+ // no instance means no observer
+ if (!this._instance) this._observe()
+ this._mount(this._def)
} else {
if (parent && parent._pendingResolve) {
this._pendingResolve = parent._pendingResolve.then(() => {
@@ -324,10 +325,13 @@ export class VueElement
}
}
- private _unmount(keepParent?: boolean) {
- if (this._ob) {
- this._ob.disconnect()
- this._ob = null
+ private _unmount(keepParentAndOb?: boolean) {
+ if (!keepParentAndOb) {
+ this._parent = undefined
+ if (this._ob) {
+ this._ob.disconnect()
+ this._ob = null
+ }
}
this._app && this._app.unmount()
if (this._instance) {
@@ -340,8 +344,6 @@ export class VueElement
this._instance.ce = undefined
}
this._app = this._instance = null
- if (!keepParent) this._parent = undefined
- this._resolved = false
}
disconnectedCallback(): void {
@@ -353,6 +355,17 @@ export class VueElement
})
}
+ private _observe() {
+ if (!this._ob) {
+ this._ob = new MutationObserver(mutations => {
+ for (const m of mutations) {
+ this._setAttr(m.attributeName!)
+ }
+ })
+ }
+ this._ob.observe(this, { attributes: true })
+ }
+
/**
* resolve inner component definition (handle possible async component)
*/
@@ -367,13 +380,7 @@ export class VueElement
}
// watch future attr changes
- this._ob = new MutationObserver(mutations => {
- for (const m of mutations) {
- this._setAttr(m.attributeName!)
- }
- })
-
- this._ob.observe(this, { attributes: true })
+ this._observe()
const resolve = (def: InnerComponentDef, isAsync = false) => {
this._resolved = true
@@ -534,7 +541,7 @@ export class VueElement
} else if (!val) {
this.removeAttribute(hyphenate(key))
}
- ob && ob.observe(this, { attributes: true })
+ this._observe()
}
}
}