Kaynağa Gözat

fix(v-model): avoid duplicate model transforms

This happens when a component directly passes down its own data object
to a child component. Fix #8436.
Evan You 7 yıl önce
ebeveyn
işleme
7b7164c11c

+ 11 - 3
src/core/vdom/create-component.js

@@ -252,9 +252,17 @@ function transformModel (options, data: any) {
   const event = (options.model && options.model.event) || 'input'
   ;(data.props || (data.props = {}))[prop] = data.model.value
   const on = data.on || (data.on = {})
-  if (isDef(on[event])) {
-    on[event] = [data.model.callback].concat(on[event])
+  const existing = on[event]
+  const callback = data.model.callback
+  if (isDef(existing)) {
+    if (
+      Array.isArray(existing)
+        ? existing.indexOf(callback) === -1
+        : existing !== callback
+    ) {
+      on[event] = [callback].concat(existing)
+    }
   } else {
-    on[event] = data.model.callback
+    on[event] = callback
   }
 }

+ 56 - 0
test/unit/features/directives/model-component.spec.js

@@ -148,4 +148,60 @@ describe('Directive v-model component', () => {
     vm.$refs.input.$emit('input', '   foo o  ')
     expect(vm.text).toBe('foo o')
   })
+
+  // #8436
+  it('should not double transform mode props', () => {
+    const BaseInput = {
+      props: ['value'],
+      render (h) {
+        return h('input', {
+          domProps: {
+            value: this.value
+          },
+          on: {
+            input: e => this.$emit('input', e.target.value)
+          }
+        })
+      }
+    }
+
+    const FunctionalWrapper = {
+      functional: true,
+      render (h, ctx) {
+        return h(BaseInput, ctx.data)
+      }
+    }
+
+    let triggerCount = 0
+
+    const vm = new Vue({
+      components: {
+        FunctionalWrapper
+      },
+      template: `
+        <div>
+          <functional-wrapper v-model="val"/>
+        </div>
+      `,
+      data: {
+        internalVal: ''
+      },
+      computed: {
+        val: {
+          get () {
+            return this.internalVal
+          },
+          set (val) {
+            triggerCount++
+            this.internalVal = val
+          }
+        }
+      }
+    }).$mount()
+
+    document.body.appendChild(vm.$el)
+    triggerEvent(vm.$el.querySelector('input'), 'input')
+    expect(triggerCount).toBe(1)
+    document.body.removeChild(vm.$el)
+  })
 })