diff --git a/src/platforms/web/runtime/modules/events.js b/src/platforms/web/runtime/modules/events.js index ecb08af3..3c60fd5d 100644 --- a/src/platforms/web/runtime/modules/events.js +++ b/src/platforms/web/runtime/modules/events.js @@ -5,6 +5,7 @@ import { updateListeners } from 'core/vdom/helpers/index' import { isIE, isFF, supportsPassive, isUsingMicroTask } from 'core/util/index' import { RANGE_TOKEN, CHECKBOX_RADIO_TOKEN } from 'web/compiler/directives/model' import { currentFlushTimestamp } from 'core/observer/scheduler' +import { emptyNode } from 'core/vdom/patch' // normalize v-model event tokens that can only be determined at runtime. // it's important to place the event as the first in the array because @@ -108,7 +109,9 @@ function updateDOMListeners (oldVnode: VNodeWithData, vnode: VNodeWithData) { } const on = vnode.data.on || {} const oldOn = oldVnode.data.on || {} - target = vnode.elm + // vnode is empty when removing all listeners, + // and use old vnode dom element + target = vnode.elm || oldVnode.elm normalizeEvents(on) updateListeners(on, oldOn, add, remove, createOnceHandler, vnode.context) target = undefined @@ -116,5 +119,6 @@ function updateDOMListeners (oldVnode: VNodeWithData, vnode: VNodeWithData) { export default { create: updateDOMListeners, - update: updateDOMListeners + update: updateDOMListeners, + destroy: (vnode: VNodeWithData) => updateDOMListeners(vnode, emptyNode) } diff --git a/test/unit/features/component/component-keep-alive.spec.js b/test/unit/features/component/component-keep-alive.spec.js index c8734958..9d30d2c7 100644 --- a/test/unit/features/component/component-keep-alive.spec.js +++ b/test/unit/features/component/component-keep-alive.spec.js @@ -1249,5 +1249,37 @@ describe('Component keep-alive', () => { }).then(done) } }) + + // #10083 + it('should not attach event handler repeatedly', done => { + const vm = new Vue({ + template: ` + + + + `, + data: { showBtn: true, n: 0 }, + methods: { + add () { + this.n++ + } + }, + components: { + btn: { template: '' } + } + }).$mount() + + const btn = vm.$el + expect(vm.n).toBe(0) + btn.click() + expect(vm.n).toBe(1) + vm.showBtn = false + waitForUpdate(() => { + vm.showBtn = true + }).then(() => { + btn.click() + expect(vm.n).toBe(2) + }).then(done) + }) } })