Evan You 11 лет назад
Родитель
Сommit
c88e5180ff
2 измененных файлов с 47 добавлено и 12 удалено
  1. 36 12
      src/api/events.js
  2. 11 0
      test/unit/specs/api/events_spec.js

+ 36 - 12
src/api/events.js

@@ -7,20 +7,10 @@ var _ = require('../util')
  * @param {Function} fn
  */
 
-var hookRE = /^hook:/
 exports.$on = function (event, fn) {
   (this._events[event] || (this._events[event] = []))
     .push(fn)
-  // hooks do not get broadcasted so we can skip them
-  if (hookRE.test(event)) return
-  // increment all parent event count by 1.
-  // pay a small cost here to optimize for $broadcast.
-  var parent = this.$parent
-  while (parent) {
-    parent._eventsCount[event] =
-      (parent._eventsCount[event] || 0) + 1
-    parent = parent.$parent
-  }
+  modifyListenerCount(this, event, 1)
 }
 
 /**
@@ -50,15 +40,25 @@ exports.$once = function (event, fn) {
  */
 
 exports.$off = function (event, fn) {
+  var cbs
   // all
   if (!arguments.length) {
+    if (this.$parent) {
+      for (event in this._events) {
+        cbs = this._events[event]
+        if (cbs) {
+          modifyListenerCount(this, event, -cbs.length)
+        }
+      }
+    }
     this._events = {}
     return
   }
   // specific event
-  var cbs = this._events[event]
+  cbs = this._events[event]
   if (!cbs) return
   if (arguments.length === 1) {
+    modifyListenerCount(this, event, -cbs.length)
     this._events[event] = null
     return
   }
@@ -68,6 +68,7 @@ exports.$off = function (event, fn) {
   while (i--) {
     cb = cbs[i]
     if (cb === fn || cb.fn === fn) {
+      modifyListenerCount(this, event, -1)
       cbs.splice(i, 1)
       break
     }
@@ -141,4 +142,27 @@ exports.$dispatch = function () {
       ? null
       : parent.$parent
   }
+}
+
+/**
+ * Modify the listener counts on all parents.
+ * This bookkeeping allows $broadcast to return early when
+ * no child has listened to a certain event.
+ *
+ * @param {Vue} vm
+ * @param {String} event
+ * @param {Number} count
+ */
+
+var hookRE = /^hook:/
+function modifyListenerCount (vm, event, count) {
+  var parent = vm.$parent
+  // hooks do not get broadcasted so no need
+  // to do bookkeeping for them
+  if (!parent || !count || hookRE.test(event)) return
+  while (parent) {
+    parent._eventsCount[event] =
+      (parent._eventsCount[event] || 0) + count
+    parent = parent.$parent
+  }
 }

+ 11 - 0
test/unit/specs/api/events_spec.js

@@ -83,6 +83,17 @@ describe('Events API', function () {
     child2.$broadcast = spy
     vm.$broadcast('test')
     expect(spy.calls.count()).toBe(1)
+    // check $off bookkeeping
+    child.$off('test', spy)
+    expect(vm._eventsCount['test']).toBe(0)
+    child.$on('test', spy)
+    expect(vm._eventsCount['test']).toBe(1)
+    child.$off('test')
+    expect(vm._eventsCount['test']).toBe(0)
+    child.$on('test', spy)
+    expect(vm._eventsCount['test']).toBe(1)
+    child.$off()
+    expect(vm._eventsCount['test']).toBe(0)
   })
 
   it('$broadcast cancel', function () {