Backbone事件模塊


事件模塊Backbone.Events是Backbone的核心,Model、Collection、View都依賴它。

此外,事件模塊的所有方法都掛在了全局的Backbone上,如果你的代碼中需要用到自定義事件(實現觀察者模式),可以直接使用它。

 

標示符Events是內部的一個引用,為討論方便,這里也省去了前綴Backbone,這篇文章會從以下幾個點分析

  • Events API
  • Events的用法
  • Events內部數據結構 (_events/_listeners)
  • 特殊事件: “all”
  • Events與Model、Collection、View、Router、History的關系

 

一、Events API

1.0之前只提供了三個基本方法 on/once/off/trigger,1.0開始增加了幾個實用方法 listenTo/listenToOnce/stopListening。

以下是各個方法的意義

  • on 添加自定義事件
  • off 刪除自定義事件
  • trigger 派發自定義事件
  • once 添加只執行一次的自定義事件 (內部依賴於_.once)
  • listenTo 添加一個觀察對象
  • listenToOnce 添加一個僅執行一次的觀察對象
  • stopListening 刪除添加的觀察對象

 

二、Events的代碼鳥瞰

var Events = Backbone.Events = {
    on: function(name, callback, context) {
		// ...
    },
    once: function(name, callback, context) {
		// ...
    },
    off: function(name, callback, context) {
		// ...
    },
    trigger: function(name) {
    	// ...
    },
    stopListening: function(obj, name, callback) {
		// ...
    }
};

var eventSplitter = /\s+/;
var eventsApi = function(obj, action, name, rest) {
	// ...
};
var triggerEvents = function(events, args) {
	// ...
};
var listenMethods = {listenTo: 'on', listenToOnce: 'once'};

_.each(listenMethods, function(implementation, method) {
    Events[method] = function(obj, name, callback) {
    	// ...
    };
});

Events.bind   = Events.on;
Events.unbind = Events.off;

_.extend(Backbone, Events);

1. 先定義了一個對象(單例),直接掛上了接口方法on/once/off/trigger/stopListening,注意雖然Events頭字母大寫,這里不是定義一個類或構造器,而是一個單例對象

    剩下的變量和函數都是輔助這個對象的,它們都在閉包空間里,外部不可訪問如eventSplitter、eventsApi等

2. eventSplitter用來實現空格間隔一次添加多個事件,如 .on('event1 event2', handler)

3. eventsApi實現的很巧妙,它輔助on/once、off、trigger完成事件的添加、刪除、派發。你會發現這里是一個遞歸調用,來實現一些批量添加事件。如

// 空格間隔批量添加多個事件
.on('event1 event2', handler)

// 哈希對象批量添加
var obj = {
    event1: handler1,
    event2: handler2,
    event3: handler3
}
.on(obj)

4. triggerEvent輔助trigger方法實現派發事件,它的實現有些特殊,見 冗余換性能

5. 后面的listenMethod和一個each迭代,會給Events添加兩個新方法listenTo和listenToOnce

6. 再下面兩行給on/off分別取了別名bind/unbind。其實這也是為了兼容老版本,最早的版本添加/刪除事件為bind/unbind。

7. 最后一行把Events摻合到全局的Backbone對象上了

拓撲圖如下

 

 

三、Events內部數據結構 (_events/_listeners)

相對來說,目前的內部數據結構比較簡單。采用傳統的先哈希,后數組存儲事件處理器對象,處理器對象上有callback和context及ctx。其內部有兩個關鍵對象_eventshe _listeners,都以下划線開頭,說明這是私有的(並非真正私有,一種語法約定,真正私有可使用閉包實現),僅供內部使用。

_events 這個哈希是默認是掛在Backbone.Events上,由於Events是一個對象,所以很容易被Mix到任何想增加自定義事件的類或對象上。此時_events則掛在該對象上。如Backbone.Model,Backbone.Collection,Backbone.View,當給其實例添加自定義事件時,_events則掛在它們的實例對象上。

 

_events的結構如下

_.extend(Backbone, Events);

 

_listeners 和 _events一樣默認也是掛在Backbone.Events上。顧名思義,它是一個監聽器,即可以為其它對象(具有Backbone.Events的所有方法的對象)被添加事件。它的key是以字母“l”開頭后跟遞增的數字組成,value是一個 “a mixin of Backbone.Events”。

 

_listeners的結構如下

 

最后又把Events上的所有方法都拷貝到標示符Backbone這個全局對象上,即給Backbone添加了如下方法。這時可以很方便的使用它給自己的類添加自定義事件。

 

四、 特殊事件“all”

事件名“all”,在trigger方法中,仔細看代碼,你會發現trigger方法中調用了兩次triggerEvents,一次是通過參數傳進來的事件,另一次則固定為“all”事件。

var events = this._events[name];
var allEvents = this._events.all;
if (events) triggerEvents(events, args);
if (allEvents) triggerEvents(allEvents, arguments);

trigger的通常實現只需把事件名,參數傳進來,取哈希(這里是_events)上取該事件的所有handlers(存在在數組里),挨個執行。但這里為什么每一次trigger調用還要單獨取下all事件,然后執行呢?

如果只看Backbone.Events模塊,是很難理解的。那么就搜索下整個Backbone.js,看“all”事件在哪些地方使用到。最后發現只在Backbone.Collection中用到,且僅一處。

model.on('all', this._onModelEvent, this);

只看這一行代碼,還是難以理解。需要結合Backbone.Model和Backbone.Collection一起看。

這里先簡單說下,我們知道這行代碼所在方法是Collection.add,在往collection中添加model時執行的,即添加的model都會注冊一個“all”事件。而當model自身銷毀(destroy)或修改(change)的時候,需要通知其所在Collection。

 

例如,model銷毀后,Collection需要在集合中把它刪除,Collection的長度也需要減一。model修改后,也需要通知Collection,這樣給Collection添加的change事件也會觸發。

這就是“all”事件的真正用途,以前曾想既然Backbone的View和通信都依賴於jQuery,那么事件模塊也完全可以使用$.Callbacks。不曾想到還有一個特殊的“all”事件。

 

五、Events與Model、Collection、View、Router、History的關系

來看下代碼

_.extend(Model.prototype, Events, {

})
_.extend(Collection.prototype, Events, {

})
_.extend(View.prototype, Events, {

})
_.extend(Router.prototype, Events, {

})

把事件模塊mixin到這幾個類的原型上去了。一句話,這些類都具有Pub/Sub的功能,即都可以實現自定義事件,它們之間也就可以通過事件很方便的降低耦合。如果在加上數據、視圖、邏輯的分層效果,這就是整個Backbone的精華了。

 

相關:

冗余換性能-從Backbone的triggerEvents說開了去


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM