]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(custom-elements): properties set pre-upgrade should not show up in $attrs
authorEvan You <yyx990803@gmail.com>
Fri, 11 Nov 2022 06:42:42 +0000 (14:42 +0800)
committerEvan You <yyx990803@gmail.com>
Fri, 11 Nov 2022 06:42:42 +0000 (14:42 +0800)
packages/runtime-dom/__tests__/customElement.spec.ts
packages/runtime-dom/src/apiCustomElement.ts

index 03a5361d69009735a6b40f2b83c35121f9120624..09ee971d225824934b7157225f589dafebd7c746 100644 (file)
@@ -230,6 +230,33 @@ describe('defineCustomElement', () => {
     })
   })
 
+  describe('attrs', () => {
+    const E = defineCustomElement({
+      render() {
+        return [h('div', null, this.$attrs.foo as string)]
+      }
+    })
+    customElements.define('my-el-attrs', E)
+
+    test('attrs via attribute', async () => {
+      container.innerHTML = `<my-el-attrs foo="hello"></my-el-attrs>`
+      const e = container.childNodes[0] as VueElement
+      expect(e.shadowRoot!.innerHTML).toBe('<div>hello</div>')
+
+      e.setAttribute('foo', 'changed')
+      await nextTick()
+      expect(e.shadowRoot!.innerHTML).toBe('<div>changed</div>')
+    })
+
+    test('non-declared properties should not show up in $attrs', () => {
+      const e = new E()
+      // @ts-ignore
+      e.foo = '123'
+      container.appendChild(e)
+      expect(e.shadowRoot!.innerHTML).toBe('<div></div>')
+    })
+  })
+
   describe('emits', () => {
     const CompDef = defineComponent({
       setup(_, { emit }) {
index 55530b98c3278ec28c1576b414c6ceaa1dd00af2..ebb1eccc0bd7d59fddde3489ff482f2a9997acab 100644 (file)
@@ -228,13 +228,12 @@ export class VueElement extends BaseClass {
     }).observe(this, { attributes: true })
 
     const resolve = (def: InnerComponentDef) => {
-      const { props = {}, styles } = def
-      const hasOptions = !isArray(props)
-      const rawKeys = props ? (hasOptions ? Object.keys(props) : props) : []
+      const { props, styles } = def
+      const declaredPropKeys = isArray(props) ? props : Object.keys(props || {})
 
       // cast Number-type props set before resolve
       let numberProps
-      if (hasOptions) {
+      if (props && !isArray(props)) {
         for (const key in this._props) {
           const opt = props[key]
           if (opt === Number || (opt && opt.type === Number)) {
@@ -247,18 +246,13 @@ export class VueElement extends BaseClass {
 
       // check if there are props set pre-upgrade or connect
       for (const key of Object.keys(this)) {
-        if (key[0] !== '_') {
-          this._setProp(
-            key,
-            this[key as keyof this],
-            rawKeys.includes(key),
-            false
-          )
+        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 rawKeys.map(camelize)) {
+      for (const key of declaredPropKeys.map(camelize)) {
         Object.defineProperty(this, key, {
           get() {
             return this._getProp(key)