Forráskód Böngészése

v-on delegation refactor, functional tests pass

Evan You 12 éve
szülő
commit
60e5154715
3 módosított fájl, 66 hozzáadás és 63 törlés
  1. 43 1
      src/compiler.js
  2. 22 61
      src/directives/on.js
  3. 1 1
      test/functional/fixtures/transition.html

+ 43 - 1
src/compiler.js

@@ -58,6 +58,7 @@ function Compiler (vm, options) {
     compiler.childCompilers = []
     compiler.emitter = new Emitter()
     compiler.emitter._ctx = vm
+    compiler.delegators = makeHash()
 
     // set inenumerable VM properties
     def(vm, '$', makeHash())
@@ -687,6 +688,41 @@ CompilerProto.parseDeps = function () {
     DepsParser.parse(this.computed)
 }
 
+/**
+ *  Add an event delegation listener
+ *  listeners are instances of directives with `isFn:true`
+ */
+CompilerProto.addListener = function (listener) {
+    var event = listener.arg,
+        delegator = this.delegators[event]
+    if (!delegator) {
+        // initialize a delegator
+        delegator = this.delegators[event] = {
+            targets: [],
+            handler: function (e) {
+                var i = delegator.targets.length,
+                    target
+                while (i--) {
+                    target = delegator.targets[i]
+                    if (e.target === target.el && target.handler) {
+                        target.handler(e)
+                    }
+                }
+            }
+        }
+        this.el.addEventListener(event, delegator.handler)
+    }
+    delegator.targets.push(listener)
+}
+
+/**
+ *  Remove an event delegation listener
+ */
+CompilerProto.removeListener = function (listener) {
+    var targets = this.delegators[listener.arg].targets
+    targets.splice(targets.indexOf(listener), 1)
+}
+
 /**
  *  Unbind and remove element
  */
@@ -702,7 +738,8 @@ CompilerProto.destroy = function () {
         el          = compiler.el,
         directives  = compiler.dirs,
         exps        = compiler.exps,
-        bindings    = compiler.bindings
+        bindings    = compiler.bindings,
+        delegators  = compiler.delegators
 
     compiler.execHook('beforeDestroy')
 
@@ -738,6 +775,11 @@ CompilerProto.destroy = function () {
         }
     }
 
+    // remove all event delegators
+    for (key in delegators) {
+        el.removeEventListener(key, delegators[key].handler)
+    }
+
     // remove self from parentCompiler
     var parent = compiler.parentCompiler,
         childId = compiler.childId

+ 22 - 61
src/directives/on.js

@@ -1,84 +1,45 @@
-var utils = require('../utils')
-
-function delegateCheck (el, root, identifier) {
-    while (el && el !== root) {
-        if (el[identifier]) return el
-        el = el.parentNode
-    }
-}
+var warn = require('utils').warn
 
 module.exports = {
 
     isFn: true,
 
     bind: function () {
-        if (this.compiler.repeat) {
-            // attach an identifier to the el
-            // so it can be matched during event delegation
-            this.el[this.expression] = true
-            // attach the owner viewmodel of this directive
-            this.el.vue_viewmodel = this.vm
+        // blur and focus events do not bubble
+        // so they can't be delegated
+        this.bubbles = this.arg !== 'blur' && this.arg !== 'focus'
+        if (this.bubbles) {
+            this.compiler.addListener(this)
         }
     },
 
     update: function (handler) {
-        this.reset()
         if (typeof handler !== 'function') {
-            return utils.warn('Directive "on" expects a function value.')
+            return warn('Directive "on" expects a function value.')
         }
-
-        var compiler = this.compiler,
-            event    = this.arg,
+        var targetVM = this.vm,
+            ownerVM  = this.binding.compiler.vm,
             isExp    = this.binding.isExp,
-            ownerVM  = this.binding.compiler.vm
-
-        if (compiler.repeat &&
-            // do not delegate if the repeat is combined with an extended VM
-            !this.vm.constructor.super &&
-            // blur and focus events do not bubble
-            event !== 'blur' && event !== 'focus') {
-
-            // for each blocks, delegate for better performance
-            // focus and blur events dont bubble so exclude them
-            var delegator  = compiler.delegator,
-                identifier = this.expression,
-                dHandler   = delegator.vue_dHandlers[identifier]
-
-            if (dHandler) return
-
-            // the following only gets run once for the entire each block
-            dHandler = delegator.vue_dHandlers[identifier] = function (e) {
-                var target = delegateCheck(e.target, delegator, identifier)
-                if (target) {
-                    e.el = target
-                    e.targetVM = target.vue_viewmodel
-                    handler.call(isExp ? e.targetVM : ownerVM, e)
-                }
+            newHandler = function (e) {
+                e.targetVM = targetVM
+                handler.call(isExp ? targetVM : ownerVM, e)
             }
-            dHandler.event = event
-            delegator.addEventListener(event, dHandler)
-
-        } else {
-
-            // a normal, single element handler
-            var vm = this.vm
-            this.handler = function (e) {
-                e.el = e.currentTarget
-                e.targetVM = vm
-                handler.call(ownerVM, e)
-            }
-            this.el.addEventListener(event, this.handler)
-
+        if (!this.bubbles) {
+            this.reset()
+            this.el.addEventListener(this.arg, newHandler)
         }
+        this.handler = newHandler
     },
 
     reset: function () {
         this.el.removeEventListener(this.arg, this.handler)
-        this.handler = null
     },
-
+    
     unbind: function () {
-        this.reset()
-        this.el.vue_viewmodel = null
+        if (this.bubbles) {
+            this.compiler.removeListener(this)
+        } else {
+            this.reset()
+        }
     }
 }

+ 1 - 1
test/functional/fixtures/transition.html

@@ -60,7 +60,7 @@
         data: {
             b: 1,
             set: function (e) {
-                this.b = +e.el.textContent
+                this.b = +e.target.textContent
             },
             push: function () {
                 this.items.push({a: this.items.length + 1 })