From 19c33a7e4072b03069f803263ed0c49feb5f73a9 Mon Sep 17 00:00:00 2001 From: laoxiong Date: Mon, 3 Dec 2018 00:32:59 +0800 Subject: [PATCH] fix(v-on): correctly remove once listener (#8036) fix #8032 --- src/core/instance/events.js | 20 ++++++++++----- src/core/vdom/helpers/update-listeners.js | 14 ++++++++-- src/platforms/web/runtime/modules/events.js | 6 ++--- src/platforms/weex/runtime/modules/events.js | 25 ++++++++---------- test/unit/features/directives/on.spec.js | 27 ++++++++++++++++++++ 5 files changed, 65 insertions(+), 27 deletions(-) diff --git a/src/core/instance/events.js b/src/core/instance/events.js index dbf45633eae..8225bdecb8e 100644 --- a/src/core/instance/events.js +++ b/src/core/instance/events.js @@ -21,25 +21,31 @@ export function initEvents (vm: Component) { let target: any -function add (event, fn, once) { - if (once) { - target.$once(event, fn) - } else { - target.$on(event, fn) - } +function add (event, fn) { + target.$on(event, fn) } function remove (event, fn) { target.$off(event, fn) } +function createOnceHandler (event, fn) { + const _target = target + return function onceHandler () { + const res = fn.apply(null, arguments) + if (res !== null) { + _target.$off(event, onceHandler) + } + } +} + export function updateComponentListeners ( vm: Component, listeners: Object, oldListeners: ?Object ) { target = vm - updateListeners(listeners, oldListeners || {}, add, remove, vm) + updateListeners(listeners, oldListeners || {}, add, remove, createOnceHandler, vm) target = undefined } diff --git a/src/core/vdom/helpers/update-listeners.js b/src/core/vdom/helpers/update-listeners.js index 11c5a2cd2f8..df58727f12b 100644 --- a/src/core/vdom/helpers/update-listeners.js +++ b/src/core/vdom/helpers/update-listeners.js @@ -1,7 +1,13 @@ /* @flow */ import { warn } from 'core/util/index' -import { cached, isUndef, isPlainObject } from 'shared/util' + +import { + cached, + isUndef, + isTrue, + isPlainObject +} from 'shared/util' const normalizeEvent = cached((name: string): { name: string, @@ -47,6 +53,7 @@ export function updateListeners ( oldOn: Object, add: Function, remove: Function, + createOnceHandler: Function, vm: Component ) { let name, def, cur, old, event @@ -68,7 +75,10 @@ export function updateListeners ( if (isUndef(cur.fns)) { cur = on[name] = createFnInvoker(cur) } - add(event.name, cur, event.once, event.capture, event.passive, event.params) + if (isTrue(event.once)) { + cur = on[name] = createOnceHandler(event.name, cur, event.capture) + } + add(event.name, cur, event.capture, event.passive, event.params) } else if (cur !== old) { old.fns = cur on[name] = old diff --git a/src/platforms/web/runtime/modules/events.js b/src/platforms/web/runtime/modules/events.js index 1e71b667286..e89a38d82c9 100644 --- a/src/platforms/web/runtime/modules/events.js +++ b/src/platforms/web/runtime/modules/events.js @@ -28,7 +28,7 @@ function normalizeEvents (on) { let target: any -function createOnceHandler (handler, event, capture) { +function createOnceHandler (event, handler, capture) { const _target = target // save current target element in closure return function onceHandler () { const res = handler.apply(null, arguments) @@ -41,12 +41,10 @@ function createOnceHandler (handler, event, capture) { function add ( event: string, handler: Function, - once: boolean, capture: boolean, passive: boolean ) { handler = withMacroTask(handler) - if (once) handler = createOnceHandler(handler, event, capture) target.addEventListener( event, handler, @@ -77,7 +75,7 @@ function updateDOMListeners (oldVnode: VNodeWithData, vnode: VNodeWithData) { const oldOn = oldVnode.data.on || {} target = vnode.elm normalizeEvents(on) - updateListeners(on, oldOn, add, remove, vnode.context) + updateListeners(on, oldOn, add, remove, createOnceHandler, vnode.context) target = undefined } diff --git a/src/platforms/weex/runtime/modules/events.js b/src/platforms/weex/runtime/modules/events.js index a3f9e25a0ac..c34719483a8 100755 --- a/src/platforms/weex/runtime/modules/events.js +++ b/src/platforms/weex/runtime/modules/events.js @@ -4,10 +4,19 @@ import { updateListeners } from 'core/vdom/helpers/update-listeners' let target: any +function createOnceHandler (event, handler, capture) { + const _target = target // save current target element in closure + return function onceHandler () { + const res = handler.apply(null, arguments) + if (res !== null) { + remove(event, onceHandler, capture, _target) + } + } +} + function add ( event: string, handler: Function, - once: boolean, capture: boolean, passive?: boolean, params?: Array @@ -16,18 +25,6 @@ function add ( console.log('Weex do not support event in bubble phase.') return } - if (once) { - const oldHandler = handler - const _target = target // save current target element in closure - handler = function (ev) { - const res = arguments.length === 1 - ? oldHandler(ev) - : oldHandler.apply(null, arguments) - if (res !== null) { - remove(event, null, null, _target) - } - } - } target.addEvent(event, handler, params) } @@ -47,7 +44,7 @@ function updateDOMListeners (oldVnode: VNodeWithData, vnode: VNodeWithData) { const on = vnode.data.on || {} const oldOn = oldVnode.data.on || {} target = vnode.elm - updateListeners(on, oldOn, add, remove, vnode.context) + updateListeners(on, oldOn, add, remove, createOnceHandler, vnode.context) target = undefined } diff --git a/test/unit/features/directives/on.spec.js b/test/unit/features/directives/on.spec.js index 3973c41cb26..807ad5777c8 100644 --- a/test/unit/features/directives/on.spec.js +++ b/test/unit/features/directives/on.spec.js @@ -895,4 +895,31 @@ describe('Directive v-on', () => { }).$mount() expect(`v-on without argument expects an Object value`).toHaveBeenWarned() }) + + it('should correctly remove once listener', done => { + const vm = new Vue({ + template: ` +
+ + a + + + b + +
+ `, + data: { + ok: true + }, + methods: { + foo: spy + } + }).$mount() + + vm.ok = false + waitForUpdate(() => { + triggerEvent(vm.$el.childNodes[0], 'click') + expect(spy.calls.count()).toBe(0) + }).then(done) + }) })