Просмотр исходного кода

fix(custom-elements): properties set pre-upgrade should not show up in $attrs

Evan You 3 лет назад
Родитель
Сommit
afe889999c

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

@@ -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 }) {

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

@@ -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)