Skip to content

Commit 60e5154

Browse files
committed
v-on delegation refactor, functional tests pass
1 parent 659593f commit 60e5154

File tree

3 files changed

+66
-63
lines changed

3 files changed

+66
-63
lines changed

src/compiler.js

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ function Compiler (vm, options) {
5858
compiler.childCompilers = []
5959
compiler.emitter = new Emitter()
6060
compiler.emitter._ctx = vm
61+
compiler.delegators = makeHash()
6162

6263
// set inenumerable VM properties
6364
def(vm, '$', makeHash())
@@ -687,6 +688,41 @@ CompilerProto.parseDeps = function () {
687688
DepsParser.parse(this.computed)
688689
}
689690

691+
/**
692+
* Add an event delegation listener
693+
* listeners are instances of directives with `isFn:true`
694+
*/
695+
CompilerProto.addListener = function (listener) {
696+
var event = listener.arg,
697+
delegator = this.delegators[event]
698+
if (!delegator) {
699+
// initialize a delegator
700+
delegator = this.delegators[event] = {
701+
targets: [],
702+
handler: function (e) {
703+
var i = delegator.targets.length,
704+
target
705+
while (i--) {
706+
target = delegator.targets[i]
707+
if (e.target === target.el && target.handler) {
708+
target.handler(e)
709+
}
710+
}
711+
}
712+
}
713+
this.el.addEventListener(event, delegator.handler)
714+
}
715+
delegator.targets.push(listener)
716+
}
717+
718+
/**
719+
* Remove an event delegation listener
720+
*/
721+
CompilerProto.removeListener = function (listener) {
722+
var targets = this.delegators[listener.arg].targets
723+
targets.splice(targets.indexOf(listener), 1)
724+
}
725+
690726
/**
691727
* Unbind and remove element
692728
*/
@@ -702,7 +738,8 @@ CompilerProto.destroy = function () {
702738
el = compiler.el,
703739
directives = compiler.dirs,
704740
exps = compiler.exps,
705-
bindings = compiler.bindings
741+
bindings = compiler.bindings,
742+
delegators = compiler.delegators
706743

707744
compiler.execHook('beforeDestroy')
708745

@@ -738,6 +775,11 @@ CompilerProto.destroy = function () {
738775
}
739776
}
740777

778+
// remove all event delegators
779+
for (key in delegators) {
780+
el.removeEventListener(key, delegators[key].handler)
781+
}
782+
741783
// remove self from parentCompiler
742784
var parent = compiler.parentCompiler,
743785
childId = compiler.childId

src/directives/on.js

Lines changed: 22 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,84 +1,45 @@
1-
var utils = require('../utils')
2-
3-
function delegateCheck (el, root, identifier) {
4-
while (el && el !== root) {
5-
if (el[identifier]) return el
6-
el = el.parentNode
7-
}
8-
}
1+
var warn = require('utils').warn
92

103
module.exports = {
114

125
isFn: true,
136

147
bind: function () {
15-
if (this.compiler.repeat) {
16-
// attach an identifier to the el
17-
// so it can be matched during event delegation
18-
this.el[this.expression] = true
19-
// attach the owner viewmodel of this directive
20-
this.el.vue_viewmodel = this.vm
8+
// blur and focus events do not bubble
9+
// so they can't be delegated
10+
this.bubbles = this.arg !== 'blur' && this.arg !== 'focus'
11+
if (this.bubbles) {
12+
this.compiler.addListener(this)
2113
}
2214
},
2315

2416
update: function (handler) {
25-
this.reset()
2617
if (typeof handler !== 'function') {
27-
return utils.warn('Directive "on" expects a function value.')
18+
return warn('Directive "on" expects a function value.')
2819
}
29-
30-
var compiler = this.compiler,
31-
event = this.arg,
20+
var targetVM = this.vm,
21+
ownerVM = this.binding.compiler.vm,
3222
isExp = this.binding.isExp,
33-
ownerVM = this.binding.compiler.vm
34-
35-
if (compiler.repeat &&
36-
// do not delegate if the repeat is combined with an extended VM
37-
!this.vm.constructor.super &&
38-
// blur and focus events do not bubble
39-
event !== 'blur' && event !== 'focus') {
40-
41-
// for each blocks, delegate for better performance
42-
// focus and blur events dont bubble so exclude them
43-
var delegator = compiler.delegator,
44-
identifier = this.expression,
45-
dHandler = delegator.vue_dHandlers[identifier]
46-
47-
if (dHandler) return
48-
49-
// the following only gets run once for the entire each block
50-
dHandler = delegator.vue_dHandlers[identifier] = function (e) {
51-
var target = delegateCheck(e.target, delegator, identifier)
52-
if (target) {
53-
e.el = target
54-
e.targetVM = target.vue_viewmodel
55-
handler.call(isExp ? e.targetVM : ownerVM, e)
56-
}
23+
newHandler = function (e) {
24+
e.targetVM = targetVM
25+
handler.call(isExp ? targetVM : ownerVM, e)
5726
}
58-
dHandler.event = event
59-
delegator.addEventListener(event, dHandler)
60-
61-
} else {
62-
63-
// a normal, single element handler
64-
var vm = this.vm
65-
this.handler = function (e) {
66-
e.el = e.currentTarget
67-
e.targetVM = vm
68-
handler.call(ownerVM, e)
69-
}
70-
this.el.addEventListener(event, this.handler)
71-
27+
if (!this.bubbles) {
28+
this.reset()
29+
this.el.addEventListener(this.arg, newHandler)
7230
}
31+
this.handler = newHandler
7332
},
7433

7534
reset: function () {
7635
this.el.removeEventListener(this.arg, this.handler)
77-
this.handler = null
7836
},
79-
37+
8038
unbind: function () {
81-
this.reset()
82-
this.el.vue_viewmodel = null
39+
if (this.bubbles) {
40+
this.compiler.removeListener(this)
41+
} else {
42+
this.reset()
43+
}
8344
}
8445
}

test/functional/fixtures/transition.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ <h1 style="margin:0">123</h1>
6060
data: {
6161
b: 1,
6262
set: function (e) {
63-
this.b = +e.el.textContent
63+
this.b = +e.target.textContent
6464
},
6565
push: function () {
6666
this.items.push({a: this.items.length + 1 })

0 commit comments

Comments
 (0)