Skip to content

Commit 19c33a7

Browse files
gebilaoxiongyyx990803
authored andcommitted
fix(v-on): correctly remove once listener (#8036)
fix #8032
1 parent 530ca1b commit 19c33a7

File tree

5 files changed

+65
-27
lines changed

5 files changed

+65
-27
lines changed

src/core/instance/events.js

+13-7
Original file line numberDiff line numberDiff line change
@@ -21,25 +21,31 @@ export function initEvents (vm: Component) {
2121

2222
let target: any
2323

24-
function add (event, fn, once) {
25-
if (once) {
26-
target.$once(event, fn)
27-
} else {
28-
target.$on(event, fn)
29-
}
24+
function add (event, fn) {
25+
target.$on(event, fn)
3026
}
3127

3228
function remove (event, fn) {
3329
target.$off(event, fn)
3430
}
3531

32+
function createOnceHandler (event, fn) {
33+
const _target = target
34+
return function onceHandler () {
35+
const res = fn.apply(null, arguments)
36+
if (res !== null) {
37+
_target.$off(event, onceHandler)
38+
}
39+
}
40+
}
41+
3642
export function updateComponentListeners (
3743
vm: Component,
3844
listeners: Object,
3945
oldListeners: ?Object
4046
) {
4147
target = vm
42-
updateListeners(listeners, oldListeners || {}, add, remove, vm)
48+
updateListeners(listeners, oldListeners || {}, add, remove, createOnceHandler, vm)
4349
target = undefined
4450
}
4551

src/core/vdom/helpers/update-listeners.js

+12-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
/* @flow */
22

33
import { warn } from 'core/util/index'
4-
import { cached, isUndef, isPlainObject } from 'shared/util'
4+
5+
import {
6+
cached,
7+
isUndef,
8+
isTrue,
9+
isPlainObject
10+
} from 'shared/util'
511

612
const normalizeEvent = cached((name: string): {
713
name: string,
@@ -47,6 +53,7 @@ export function updateListeners (
4753
oldOn: Object,
4854
add: Function,
4955
remove: Function,
56+
createOnceHandler: Function,
5057
vm: Component
5158
) {
5259
let name, def, cur, old, event
@@ -68,7 +75,10 @@ export function updateListeners (
6875
if (isUndef(cur.fns)) {
6976
cur = on[name] = createFnInvoker(cur)
7077
}
71-
add(event.name, cur, event.once, event.capture, event.passive, event.params)
78+
if (isTrue(event.once)) {
79+
cur = on[name] = createOnceHandler(event.name, cur, event.capture)
80+
}
81+
add(event.name, cur, event.capture, event.passive, event.params)
7282
} else if (cur !== old) {
7383
old.fns = cur
7484
on[name] = old

src/platforms/web/runtime/modules/events.js

+2-4
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ function normalizeEvents (on) {
2828

2929
let target: any
3030

31-
function createOnceHandler (handler, event, capture) {
31+
function createOnceHandler (event, handler, capture) {
3232
const _target = target // save current target element in closure
3333
return function onceHandler () {
3434
const res = handler.apply(null, arguments)
@@ -41,12 +41,10 @@ function createOnceHandler (handler, event, capture) {
4141
function add (
4242
event: string,
4343
handler: Function,
44-
once: boolean,
4544
capture: boolean,
4645
passive: boolean
4746
) {
4847
handler = withMacroTask(handler)
49-
if (once) handler = createOnceHandler(handler, event, capture)
5048
target.addEventListener(
5149
event,
5250
handler,
@@ -77,7 +75,7 @@ function updateDOMListeners (oldVnode: VNodeWithData, vnode: VNodeWithData) {
7775
const oldOn = oldVnode.data.on || {}
7876
target = vnode.elm
7977
normalizeEvents(on)
80-
updateListeners(on, oldOn, add, remove, vnode.context)
78+
updateListeners(on, oldOn, add, remove, createOnceHandler, vnode.context)
8179
target = undefined
8280
}
8381

src/platforms/weex/runtime/modules/events.js

+11-14
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,19 @@ import { updateListeners } from 'core/vdom/helpers/update-listeners'
44

55
let target: any
66

7+
function createOnceHandler (event, handler, capture) {
8+
const _target = target // save current target element in closure
9+
return function onceHandler () {
10+
const res = handler.apply(null, arguments)
11+
if (res !== null) {
12+
remove(event, onceHandler, capture, _target)
13+
}
14+
}
15+
}
16+
717
function add (
818
event: string,
919
handler: Function,
10-
once: boolean,
1120
capture: boolean,
1221
passive?: boolean,
1322
params?: Array<any>
@@ -16,18 +25,6 @@ function add (
1625
console.log('Weex do not support event in bubble phase.')
1726
return
1827
}
19-
if (once) {
20-
const oldHandler = handler
21-
const _target = target // save current target element in closure
22-
handler = function (ev) {
23-
const res = arguments.length === 1
24-
? oldHandler(ev)
25-
: oldHandler.apply(null, arguments)
26-
if (res !== null) {
27-
remove(event, null, null, _target)
28-
}
29-
}
30-
}
3128
target.addEvent(event, handler, params)
3229
}
3330

@@ -47,7 +44,7 @@ function updateDOMListeners (oldVnode: VNodeWithData, vnode: VNodeWithData) {
4744
const on = vnode.data.on || {}
4845
const oldOn = oldVnode.data.on || {}
4946
target = vnode.elm
50-
updateListeners(on, oldOn, add, remove, vnode.context)
47+
updateListeners(on, oldOn, add, remove, createOnceHandler, vnode.context)
5148
target = undefined
5249
}
5350

test/unit/features/directives/on.spec.js

+27
Original file line numberDiff line numberDiff line change
@@ -895,4 +895,31 @@ describe('Directive v-on', () => {
895895
}).$mount()
896896
expect(`v-on without argument expects an Object value`).toHaveBeenWarned()
897897
})
898+
899+
it('should correctly remove once listener', done => {
900+
const vm = new Vue({
901+
template: `
902+
<div>
903+
<span v-if="ok" @click.once="foo">
904+
a
905+
</span>
906+
<span v-else a="a">
907+
b
908+
</span>
909+
</div>
910+
`,
911+
data: {
912+
ok: true
913+
},
914+
methods: {
915+
foo: spy
916+
}
917+
}).$mount()
918+
919+
vm.ok = false
920+
waitForUpdate(() => {
921+
triggerEvent(vm.$el.childNodes[0], 'click')
922+
expect(spy.calls.count()).toBe(0)
923+
}).then(done)
924+
})
898925
})

0 commit comments

Comments
 (0)