Explorar el Código

fix(custom-element): set prop runs pending mutations before disconnect (#13897)

close #13315
Alex Snezhko hace 7 meses
padre
commit
c4a88cdd0d

+ 25 - 0
packages/runtime-dom/__tests__/customElement.spec.ts

@@ -223,6 +223,31 @@ describe('defineCustomElement', () => {
       expect(e.getAttribute('baz-qux')).toBe('four')
     })
 
+    test('props via attributes and properties changed together', async () => {
+      const e = new E()
+      e.foo = 'foo1'
+      e.bar = { x: 'bar1' }
+      container.appendChild(e)
+      await nextTick()
+      expect(e.shadowRoot!.innerHTML).toBe('<div>foo1</div><div>bar1</div>')
+
+      // change attr then property
+      e.setAttribute('foo', 'foo2')
+      e.bar = { x: 'bar2' }
+      await nextTick()
+      expect(e.shadowRoot!.innerHTML).toBe('<div>foo2</div><div>bar2</div>')
+      expect(e.getAttribute('foo')).toBe('foo2')
+      expect(e.hasAttribute('bar')).toBe(false)
+
+      // change prop then attr
+      e.bar = { x: 'bar3' }
+      e.setAttribute('foo', 'foo3')
+      await nextTick()
+      expect(e.shadowRoot!.innerHTML).toBe('<div>foo3</div><div>bar3</div>')
+      expect(e.getAttribute('foo')).toBe('foo3')
+      expect(e.hasAttribute('bar')).toBe(false)
+    })
+
     test('props via hyphen property', async () => {
       const Comp = defineCustomElement({
         props: {

+ 11 - 6
packages/runtime-dom/src/apiCustomElement.ts

@@ -346,6 +346,12 @@ export class VueElement
     })
   }
 
+  private _processMutations(mutations: MutationRecord[]) {
+    for (const m of mutations) {
+      this._setAttr(m.attributeName!)
+    }
+  }
+
   /**
    * resolve inner component definition (handle possible async component)
    */
@@ -360,11 +366,7 @@ export class VueElement
     }
 
     // watch future attr changes
-    this._ob = new MutationObserver(mutations => {
-      for (const m of mutations) {
-        this._setAttr(m.attributeName!)
-      }
-    })
+    this._ob = new MutationObserver(this._processMutations.bind(this))
 
     this._ob.observe(this, { attributes: true })
 
@@ -514,7 +516,10 @@ export class VueElement
       // reflect
       if (shouldReflect) {
         const ob = this._ob
-        ob && ob.disconnect()
+        if (ob) {
+          this._processMutations(ob.takeRecords())
+          ob.disconnect()
+        }
         if (val === true) {
           this.setAttribute(hyphenate(key), '')
         } else if (typeof val === 'string' || typeof val === 'number') {