Repository: vuejs/vue
Pull request: Prevent unecessary input trigger with v-model

Fix #5586

<!--
Please make sure to read the Pull Request Guidelines:
https://github.com/vuejs/vue/blob/dev/.github/CONTRIBUTING.md#pull-request-guidelines
-->

<!-- PULL REQUEST TEMPLATE -->
<!-- (Update "[ ]" to "[x]" to check a box) -->

**What kind of change does this PR introduce?** (check at least one)

- [x] Bugfix
- [ ] Feature
- [ ] Code style update
- [ ] Refactor
- [ ] Build-related changes
- [ ] Other, please describe:

**Does this PR introduce a breaking change?** (check one)

- [ ] Yes
- [x] No

If yes, please describe the impact and migration path for existing applications:

**The PR fulfills these requirements:**

- [x] It's submitted to the `dev` branch for v2.x (or to a previous version branch), _not_ the `master` branch
- [x] When resolving a specific issue, it's referenced in the PR's title (e.g. `fix #xxx[,#xxx]`, where "xxx" is the issue number)
- [ ] All tests are passing: https://github.com/vuejs/vue/blob/dev/.github/CONTRIBUTING.md#development-setup
- [ ] New/updated tests are included

If adding a **new feature**, the PR's description includes:
- [ ] A convincing reason for adding this feature (to avoid wasting your time, it's best to open a suggestion issue first and wait for approval before working on it)

**Other information:**


Changed files:
filename: src/platforms/web/runtime/directives/model.js
patch: @@ -119,6 +119,8 @@ function onCompositionStart (e) {
 }
 
 function onCompositionEnd (e) {
+  // prevent triggering an input event for no reason
+  if (!e.target.composing) return
   e.target.composing = false
   trigger(e.target, 'input')
 }

filename: test/unit/features/directives/model-text.spec.js
patch: @@ -249,4 +249,49 @@ describe('Directive v-model text', () => {
     }).$mount()
     expect('You are binding v-model directly to a v-for iteration alias').toHaveBeenWarned()
   })
+
+  it('does not trigger extra input events with single compositionend', () => {
+    const spy = jasmine.createSpy()
+    const vm = new Vue({
+      data: {
+        a: 'a'
+      },
+      template: '<input v-model="a" @input="onInput">',
+      methods: {
+        onInput (e) {
+          spy(e.target.value)
+        }
+      }
+    }).$mount()
+    console.log(spy.calls.count())
+    expect(spy.calls.count()).toBe(0)
+    vm.$el.value = 'b'
+    triggerEvent(vm.$el, 'input')
+    expect(spy.calls.count()).toBe(1)
+    triggerEvent(vm.$el, 'compositionend')
+    expect(spy.calls.count()).toBe(1)
+  })
+
+  it('triggers extra input on compositionstart + end', () => {
+    const spy = jasmine.createSpy()
+    const vm = new Vue({
+      data: {
+        a: 'a'
+      },
+      template: '<input v-model="a" @input="onInput">',
+      methods: {
+        onInput (e) {
+          spy(e.target.value)
+        }
+      }
+    }).$mount()
+    console.log(spy.calls.count())
+    expect(spy.calls.count()).toBe(0)
+    vm.$el.value = 'b'
+    triggerEvent(vm.$el, 'input')
+    expect(spy.calls.count()).toBe(1)
+    triggerEvent(vm.$el, 'compositionstart')
+    triggerEvent(vm.$el, 'compositionend')
+    expect(spy.calls.count()).toBe(2)
+  })
 })