]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(custom-elements): define declared properties in constructor (#5328)
authorThorsten Lünborg <t.luenborg@googlemail.com>
Fri, 11 Nov 2022 07:46:01 +0000 (08:46 +0100)
committerGitHub <noreply@github.com>
Fri, 11 Nov 2022 07:46:01 +0000 (02:46 -0500)
packages/runtime-dom/__tests__/customElement.spec.ts
packages/runtime-dom/src/apiCustomElement.ts

index 6c991b9f5230736b9fb9b47090957892e4f9268e..a3d35790f31259a494f57cda1274f70669c0007a 100644 (file)
@@ -215,6 +215,30 @@ describe('defineCustomElement', () => {
       expect(el.hasAttribute('not-prop')).toBe(false)
     })
 
+    test('handle properties set before connecting', () => {
+      const obj = { a: 1 }
+      const E = defineCustomElement({
+        props: {
+          foo: String,
+          post: Object
+        },
+        setup(props) {
+          expect(props.foo).toBe('hello')
+          expect(props.post).toBe(obj)
+        },
+        render() {
+          return JSON.stringify(this.post)
+        }
+      })
+      customElements.define('my-el-preconnect', E)
+      const el = document.createElement('my-el-preconnect') as any
+      el.foo = 'hello'
+      el.post = obj
+
+      container.appendChild(el)
+      expect(el.shadowRoot.innerHTML).toBe(JSON.stringify(obj))
+    })
+
     // https://github.com/vuejs/core/issues/6163
     test('handle components with no props', async () => {
       const E = defineCustomElement({
index ecb558b17a4f93e9e67f422fb5df5514619c785b..4de753e38bed4f532fe3a2b4f8e652180d057382 100644 (file)
@@ -186,6 +186,10 @@ export class VueElement extends BaseClass {
         )
       }
       this.attachShadow({ mode: 'open' })
+      if (!(this._def as ComponentOptions).__asyncLoader) {
+        // for sync component defs we can immediately resolve props
+        this._resolveProps(this._def)
+      }
     }
   }
 
@@ -227,9 +231,8 @@ export class VueElement extends BaseClass {
       }
     }).observe(this, { attributes: true })
 
-    const resolve = (def: InnerComponentDef) => {
+    const resolve = (def: InnerComponentDef, isAsync = false) => {
       const { props, styles } = def
-      const declaredPropKeys = isArray(props) ? props : Object.keys(props || {})
 
       // cast Number-type props set before resolve
       let numberProps
@@ -248,23 +251,10 @@ export class VueElement extends BaseClass {
       }
       this._numberProps = numberProps
 
-      // check if there are props set pre-upgrade or connect
-      for (const key of Object.keys(this)) {
-        if (key[0] !== '_' && declaredPropKeys.includes(key)) {
-          this._setProp(key, this[key as keyof this], true, false)
-        }
-      }
-
-      // defining getter/setters on prototype
-      for (const key of declaredPropKeys.map(camelize)) {
-        Object.defineProperty(this, key, {
-          get() {
-            return this._getProp(key)
-          },
-          set(val) {
-            this._setProp(key, val)
-          }
-        })
+      if (isAsync) {
+        // defining getter/setters on prototype
+        // for sync defs, this already happened in the constructor
+        this._resolveProps(def)
       }
 
       // apply CSS
@@ -276,12 +266,36 @@ export class VueElement extends BaseClass {
 
     const asyncDef = (this._def as ComponentOptions).__asyncLoader
     if (asyncDef) {
-      asyncDef().then(resolve)
+      asyncDef().then(def => resolve(def, true))
     } else {
       resolve(this._def)
     }
   }
 
+  private _resolveProps(def: InnerComponentDef) {
+    const { props } = def
+    const declaredPropKeys = isArray(props) ? props : Object.keys(props || {})
+
+    // check if there are props set pre-upgrade or connect
+    for (const key of Object.keys(this)) {
+      if (key[0] !== '_' && declaredPropKeys.includes(key)) {
+        this._setProp(key, this[key as keyof this], true, false)
+      }
+    }
+
+    // defining getter/setters on prototype
+    for (const key of declaredPropKeys.map(camelize)) {
+      Object.defineProperty(this, key, {
+        get() {
+          return this._getProp(key)
+        },
+        set(val) {
+          this._setProp(key, val)
+        }
+      })
+    }
+  }
+
   protected _setAttr(key: string) {
     let value = this.getAttribute(key)
     const camelKey = camelize(key)