Skip to content

Commit c4a88cd

Browse files
authored
fix(custom-element): set prop runs pending mutations before disconnect (#13897)
close #13315
1 parent e388f1a commit c4a88cd

File tree

2 files changed

+36
-6
lines changed

2 files changed

+36
-6
lines changed

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

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,31 @@ describe('defineCustomElement', () => {
223223
expect(e.getAttribute('baz-qux')).toBe('four')
224224
})
225225

226+
test('props via attributes and properties changed together', async () => {
227+
const e = new E()
228+
e.foo = 'foo1'
229+
e.bar = { x: 'bar1' }
230+
container.appendChild(e)
231+
await nextTick()
232+
expect(e.shadowRoot!.innerHTML).toBe('<div>foo1</div><div>bar1</div>')
233+
234+
// change attr then property
235+
e.setAttribute('foo', 'foo2')
236+
e.bar = { x: 'bar2' }
237+
await nextTick()
238+
expect(e.shadowRoot!.innerHTML).toBe('<div>foo2</div><div>bar2</div>')
239+
expect(e.getAttribute('foo')).toBe('foo2')
240+
expect(e.hasAttribute('bar')).toBe(false)
241+
242+
// change prop then attr
243+
e.bar = { x: 'bar3' }
244+
e.setAttribute('foo', 'foo3')
245+
await nextTick()
246+
expect(e.shadowRoot!.innerHTML).toBe('<div>foo3</div><div>bar3</div>')
247+
expect(e.getAttribute('foo')).toBe('foo3')
248+
expect(e.hasAttribute('bar')).toBe(false)
249+
})
250+
226251
test('props via hyphen property', async () => {
227252
const Comp = defineCustomElement({
228253
props: {

packages/runtime-dom/src/apiCustomElement.ts

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,12 @@ export class VueElement
346346
})
347347
}
348348

349+
private _processMutations(mutations: MutationRecord[]) {
350+
for (const m of mutations) {
351+
this._setAttr(m.attributeName!)
352+
}
353+
}
354+
349355
/**
350356
* resolve inner component definition (handle possible async component)
351357
*/
@@ -360,11 +366,7 @@ export class VueElement
360366
}
361367

362368
// watch future attr changes
363-
this._ob = new MutationObserver(mutations => {
364-
for (const m of mutations) {
365-
this._setAttr(m.attributeName!)
366-
}
367-
})
369+
this._ob = new MutationObserver(this._processMutations.bind(this))
368370

369371
this._ob.observe(this, { attributes: true })
370372

@@ -514,7 +516,10 @@ export class VueElement
514516
// reflect
515517
if (shouldReflect) {
516518
const ob = this._ob
517-
ob && ob.disconnect()
519+
if (ob) {
520+
this._processMutations(ob.takeRecords())
521+
ob.disconnect()
522+
}
518523
if (val === true) {
519524
this.setAttribute(hyphenate(key), '')
520525
} else if (typeof val === 'string' || typeof val === 'number') {

0 commit comments

Comments
 (0)