Skip to content

Commit 3c23328

Browse files
committed
fix(custom-element): change solution
1 parent 53077a5 commit 3c23328

File tree

2 files changed

+41
-26
lines changed

2 files changed

+41
-26
lines changed

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

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -147,10 +147,12 @@ describe('defineCustomElement', () => {
147147
})
148148

149149
// #12412
150+
const contextElStyle = ':host { color: red }'
150151
const ContextEl = defineCustomElement({
151152
props: {
152153
msg: String,
153154
},
155+
styles: [contextElStyle],
154156
setup(props, { expose }) {
155157
expose({
156158
text: () => props.msg,
@@ -171,27 +173,33 @@ describe('defineCustomElement', () => {
171173
await nextTick()
172174
await nextTick() // wait two ticks for disconnect
173175
expect('text' in parent).toBe(false)
176+
expect(child.shadowRoot!.querySelectorAll('style').length).toBe(1)
174177
container.appendChild(parent) // should not throw Error
175178
await nextTick()
176179
expect(parent.text()).toBe('msg1')
177-
expect(parent.shadowRoot!.textContent).toBe('msg1')
178-
expect(child.shadowRoot!.textContent).toBe('msg1')
180+
expect(parent.shadowRoot!.textContent).toBe(contextElStyle + 'msg1')
181+
expect(child.shadowRoot!.textContent).toBe(contextElStyle + 'msg1')
179182
parent.setAttribute('msg', 'msg2')
180183
await nextTick()
181-
expect(parent.shadowRoot!.textContent).toBe('msg2')
184+
expect(parent.shadowRoot!.textContent).toBe(contextElStyle + 'msg2')
182185
await nextTick()
183-
expect(child.shadowRoot!.textContent).toBe('msg2')
186+
expect(child.shadowRoot!.textContent).toBe(contextElStyle + 'msg2')
187+
expect(child.shadowRoot!.querySelectorAll('style').length).toBe(1)
184188
})
185189

186-
test('move element to change parent and context', async () => {
190+
test('move element to new parent', async () => {
187191
container.innerHTML = `<my-context-el msg="msg1"></my-context-el><my-context-el msg="msg2"></my-context-el>`
188192
const first = container.children[0] as VueElement,
189-
second = container.children[1] as VueElement
193+
second = container.children[1] as VueElement & { text: () => string }
190194
await nextTick()
191-
expect(second.shadowRoot!.textContent).toBe('msg2')
195+
expect(second.shadowRoot!.textContent).toBe(contextElStyle + 'msg2')
192196
first.append(second)
193197
await nextTick()
194-
expect(second.shadowRoot!.textContent).toBe('msg1')
198+
expect(second.shadowRoot!.textContent).toBe(contextElStyle + 'msg1')
199+
expect(second.shadowRoot!.querySelectorAll('style').length).toBe(1)
200+
second.setAttribute('msg', 'msg3')
201+
await nextTick()
202+
expect(second.text()).toBe('msg3')
195203
})
196204
})
197205

packages/runtime-dom/src/apiCustomElement.ts

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -298,12 +298,13 @@ export class VueElement
298298
}
299299
}
300300

301-
// unmount if parent changed and previously mounted, should keep parent
301+
// unmount if parent changed and previously mounted, should keep parent and observer
302302
if (this._instance && parentChanged) this._unmount(true)
303-
if (!this._instance) {
303+
if (!this._instance || parentChanged) {
304304
if (this._resolved) {
305-
this._setParent()
306-
this._update()
305+
// no instance means no observer
306+
if (!this._instance) this._observe()
307+
this._mount(this._def)
307308
} else {
308309
if (parent && parent._pendingResolve) {
309310
this._pendingResolve = parent._pendingResolve.then(() => {
@@ -324,10 +325,13 @@ export class VueElement
324325
}
325326
}
326327

327-
private _unmount(keepParent?: boolean) {
328-
if (this._ob) {
329-
this._ob.disconnect()
330-
this._ob = null
328+
private _unmount(keepParentAndOb?: boolean) {
329+
if (!keepParentAndOb) {
330+
this._parent = undefined
331+
if (this._ob) {
332+
this._ob.disconnect()
333+
this._ob = null
334+
}
331335
}
332336
this._app && this._app.unmount()
333337
if (this._instance) {
@@ -340,8 +344,6 @@ export class VueElement
340344
this._instance.ce = undefined
341345
}
342346
this._app = this._instance = null
343-
if (!keepParent) this._parent = undefined
344-
this._resolved = false
345347
}
346348

347349
disconnectedCallback(): void {
@@ -353,6 +355,17 @@ export class VueElement
353355
})
354356
}
355357

358+
private _observe() {
359+
if (!this._ob) {
360+
this._ob = new MutationObserver(mutations => {
361+
for (const m of mutations) {
362+
this._setAttr(m.attributeName!)
363+
}
364+
})
365+
}
366+
this._ob.observe(this, { attributes: true })
367+
}
368+
356369
/**
357370
* resolve inner component definition (handle possible async component)
358371
*/
@@ -367,13 +380,7 @@ export class VueElement
367380
}
368381

369382
// watch future attr changes
370-
this._ob = new MutationObserver(mutations => {
371-
for (const m of mutations) {
372-
this._setAttr(m.attributeName!)
373-
}
374-
})
375-
376-
this._ob.observe(this, { attributes: true })
383+
this._observe()
377384

378385
const resolve = (def: InnerComponentDef, isAsync = false) => {
379386
this._resolved = true
@@ -534,7 +541,7 @@ export class VueElement
534541
} else if (!val) {
535542
this.removeAttribute(hyphenate(key))
536543
}
537-
ob && ob.observe(this, { attributes: true })
544+
this._observe()
538545
}
539546
}
540547
}

0 commit comments

Comments
 (0)