no longer need an internal emitter

This commit is contained in:
Evan You 2014-09-02 03:57:36 -07:00
parent c4412b55e2
commit 01e6e9ef77
8 changed files with 125 additions and 187 deletions

View File

@ -45,7 +45,6 @@
"src/directives/text.js",
"src/directives/transition.js",
"src/directives/with.js",
"src/emitter.js",
"src/filters/array-filters.js",
"src/filters/index.js",
"src/instance/events.js",

View File

@ -1,18 +1,102 @@
var _ = require('../util')
/**
* Proxy basic event methods on the internal emitter.
* Listen on the given `event` with `fn`.
*
* @param {String} event
* @param {Function} fn
*/
;['emit', 'on', 'off', 'once'].forEach(function (method) {
var realMethod = method === 'emit'
? 'applyEmit'
: method
exports['$' + method] = function () {
this._emitter[realMethod].apply(
this._emitter,
arguments
)
exports.$on = function (event, fn) {
(this._events[event] || (this._events[event] = []))
.push(fn)
// 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
}
})
}
/**
* Adds an `event` listener that will be invoked a single
* time then automatically removed.
*
* @param {String} event
* @param {Function} fn
*/
exports.$once = function (event, fn) {
var self = this
function on () {
self.$off(event, on)
fn.apply(this, arguments)
}
on.fn = fn
this.$on(event, on)
}
/**
* Remove the given callback for `event` or all
* registered callbacks.
*
* @param {String} event
* @param {Function} fn
*/
exports.$off = function (event, fn) {
// all
if (!arguments.length) {
this._events = {}
return
}
// specific event
var cbs = this._events[event]
if (!cbs) return
if (arguments.length === 1) {
this._events[event] = null
return
}
// specific handler
var cb
var i = cbs.length
while (i--) {
cb = cbs[i]
if (cb === fn || cb.fn === fn) {
cbs.splice(i, 1)
break
}
}
}
/**
* Trigger an event on self.
*
* @param {String} event
*/
exports.$emit = function (event) {
this._cancelled = false
var cbs = this._events[event]
if (cbs) {
// avoid leaking arguments:
// http://jsperf.com/closure-with-arguments
var i = arguments.length - 1
var args = new Array(i)
while (i--) {
args[i] = arguments[i + 1]
}
cbs = _.toArray(cbs)
i = 0
for (var l = cbs.length; i < l; i++) {
if (cbs[i].apply(this, args) === false) {
this._eventCancelled = true
}
}
}
}
/**
* Recursively broadcast an event to all children instances.
@ -21,13 +105,15 @@
* @param {...*} additional arguments
*/
exports.$broadcast = function () {
exports.$broadcast = function (event) {
// if no child has registered for this event,
// then there's no need to broadcast.
if (!this._eventsCount[event]) return
var children = this._children
for (var i = 0, l = children.length; i < l; i++) {
var child = children[i]
var emitter = child._emitter
emitter.applyEmit.apply(emitter, arguments)
if (!emitter._cancelled) {
if (children) {
for (var i = 0, l = children.length; i < l; i++) {
var child = children[i]
child.$emit.apply(child, arguments)
child.$broadcast.apply(child, arguments)
}
}
@ -41,12 +127,9 @@ exports.$broadcast = function () {
*/
exports.$dispatch = function () {
var emitter = this._emitter
emitter.applyEmit.apply(emitter, arguments)
if (!emitter._cancelled) {
var parent = this.$parent
if (parent) {
parent.$dispatch.apply(parent, arguments)
}
var parent = this.$parent
while (parent) {
parent.$emit.apply(parent, arguments)
parent = parent.$parent
}
}

View File

@ -35,7 +35,7 @@ exports.$mount = function (el) {
this._callHook('attached')
ready.call(this)
} else {
this._emitter.once('hook:attached', ready)
this.$once('hook:attached', ready)
}
}
@ -133,6 +133,5 @@ exports.$destroy = function (remove) {
this._isDestroyed = true
this._callHook('afterDestroy')
// turn off all instance listeners.
this._emitter.off()
this._emitter = null
this.$off()
}

View File

@ -4,7 +4,7 @@ module.exports = {
bind: function () {
var el = this.el
this.vm._emitter.once('hook:compiled', function () {
this.vm.$once('hook:compiled', function () {
el.removeAttribute(config.prefix + 'cloak')
})
}

View File

@ -1,143 +0,0 @@
var _ = require('./util')
/**
* Simple event emitter based on component/emitter.
*
* @constructor
* @param {Object} ctx - the context to call listners with.
*/
function Emitter (ctx) {
this._cancelled = false
this._ctx = ctx || null
}
var p = Emitter.prototype
/**
* Listen on the given `event` with `fn`.
*
* @param {String} event
* @param {Function} fn
* @return {Emitter}
*/
p.on = function (event, fn) {
this._cbs = this._cbs || {}
;(this._cbs[event] || (this._cbs[event] = []))
.push(fn)
return this
}
/**
* Adds an `event` listener that will be invoked a single
* time then automatically removed.
*
* @param {String} event
* @param {Function} fn
* @return {Emitter}
*/
p.once = function (event, fn) {
var self = this
this._cbs = this._cbs || {}
function on () {
self.off(event, on)
fn.apply(this, arguments)
}
on.fn = fn
this.on(event, on)
return this
}
/**
* Remove the given callback for `event` or all
* registered callbacks.
*
* @param {String} event
* @param {Function} fn
* @return {Emitter}
*/
p.off = function (event, fn) {
this._cbs = this._cbs || {}
// all
if (!arguments.length) {
this._cbs = {}
return this
}
// specific event
var callbacks = this._cbs[event]
if (!callbacks) return this
// remove all handlers
if (arguments.length === 1) {
this._cbs[event] = null
return this
}
// remove specific handler
var cb
var i = callbacks.length
while (i--) {
cb = callbacks[i]
if (cb === fn || cb.fn === fn) {
callbacks.splice(i, 1)
break
}
}
return this
}
/**
* The internal, faster emit with fixed amount of arguments
* using Function.call. This emit assumes that callbacks
* triggered will not modify the callback list being
* iterated through.
*
* @param {Object} event
* @return {Emitter}
*/
p.emit = function (event, a, b, c, d) {
this._cbs = this._cbs || {}
var callbacks = this._cbs[event]
if (callbacks) {
var ctx = this._ctx
for (var i = 0, l = callbacks.length; i < l; i++) {
callbacks[i].call(ctx, a, b, c, d)
}
}
return this
}
/**
* The external emit using Function.apply, used
* by Vue instance event methods.
*
* @param {Object} event
* @return {Emitter}
*/
p.applyEmit = function (event) {
this._cancelled = false
this._cbs = this._cbs || {}
var callbacks = this._cbs[event]
if (callbacks) {
// avoid leaking arguments:
// http://jsperf.com/closure-with-arguments
var i = arguments.length - 1
var args = new Array(i)
while (i--) {
args[i] = arguments[i + 1]
}
callbacks = _.toArray(callbacks)
i = 0
for (var l = callbacks.length; i < l; i++) {
if (callbacks[i].apply(this._ctx, args) === false) {
this._cancelled = true
}
}
}
return this
}
module.exports = Emitter

View File

@ -8,7 +8,6 @@ var inDoc = require('../util').inDoc
exports._initEvents = function () {
var options = this.$options
var emitter = this._emitter
var events = options.events
var methods = options.methods
if (events) {
@ -19,7 +18,7 @@ exports._initEvents = function () {
var handler = typeof handlers[i] === 'string'
? methods && methods[handlers[i]]
: handlers[i]
emitter.on(e, handler)
this.$on(e, handler)
}
}
}
@ -30,8 +29,7 @@ exports._initEvents = function () {
*/
exports._initDOMHooks = function () {
var emitter = this._emitter
emitter.on('hook:attached', function () {
this.$on('hook:attached', function () {
this._isAttached = true
var children = this._children
if (!children) return
@ -42,7 +40,7 @@ exports._initDOMHooks = function () {
}
}
})
emitter.on('hook:detached', function () {
this.$on('hook:detached', function () {
this._isAttached = false
var children = this._children
if (!children) return
@ -68,5 +66,5 @@ exports._callHook = function (hook) {
handlers[i].call(this)
}
}
this._emitter.emit('hook:' + hook)
this.$emit('hook:' + hook)
}

View File

@ -1,4 +1,3 @@
var Emitter = require('../emitter')
var mergeOptions = require('../util/merge-option')
/**
@ -17,13 +16,17 @@ exports._init = function (options) {
options = options || {}
this.$el = null
this.$ = {}
this.$root = this.$root || this
this._emitter = new Emitter(this)
this._watchers = Object.create(null)
this._userWatchers = Object.create(null)
this.$ = {}
this._watchers = {}
this._userWatchers = {}
this._directives = []
// events bookkeeping
this._events = {}
this._eventsCount = {}
this._eventCancelled = false
// block instance properties
this._blockStart =
this._blockEnd = null

View File

@ -18,10 +18,9 @@ var OBJECT = 1
* object's property keys into getter/setters that
* collect dependencies and dispatches updates.
*
* @constructor
* @extends Emitter
* @param {Array|Object} value
* @param {Number} type
* @constructor
*/
function Observer (value, type) {
@ -190,7 +189,6 @@ p.convert = function (key, val) {
binding.notify()
}
})
return binding
}
/**
@ -209,8 +207,8 @@ p.convert = function (key, val) {
p.tryRelease = function () {
if (!this.parentCount && !this.vmCount) {
var value = this.value
value.__ob__ = null
if (_.isArray(value)) {
value.__ob__.bindings = null
this.unobserveArray(value)
} else {
for (var key in value) {
@ -220,6 +218,7 @@ p.tryRelease = function () {
_.define(value, key, val, true)
}
}
value.__ob__ = null
}
}