Skip to content

Commit

Permalink
fix(v-on): correctly remove once listener (vuejs#8036)
Browse files Browse the repository at this point in the history
  • Loading branch information
gebilaoxiong authored and aJean committed Aug 19, 2020
1 parent 0c632b9 commit ab0be7a
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 27 deletions.
20 changes: 13 additions & 7 deletions src/core/instance/events.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down
14 changes: 12 additions & 2 deletions src/core/vdom/helpers/update-listeners.js
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -47,6 +53,7 @@ export function updateListeners (
oldOn: Object,
add: Function,
remove: Function,
createOnceHandler: Function,
vm: Component
) {
let name, def, cur, old, event
Expand All @@ -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
Expand Down
6 changes: 2 additions & 4 deletions src/platforms/web/runtime/modules/events.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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,
Expand Down Expand Up @@ -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
}

Expand Down
25 changes: 11 additions & 14 deletions src/platforms/weex/runtime/modules/events.js
Original file line number Diff line number Diff line change
Expand Up @@ -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<any>
Expand All @@ -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)
}

Expand All @@ -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
}

Expand Down
27 changes: 27 additions & 0 deletions test/unit/features/directives/on.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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: `
<div>
<span v-if="ok" @click.once="foo">
a
</span>
<span v-else a="a">
b
</span>
</div>
`,
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)
})
})

0 comments on commit ab0be7a

Please sign in to comment.