在上篇文章給大家介紹了Backbone中View之間傳值的學習心得。本文重點給大家介紹Backbone View 之間通信的三種方式。
掌握一個 MVC 框架,最關鍵的一節就是掌握如何在各個 View 之間通信。之前用 Angular 時,覺得基於事件的通信方式 ($on, $emit, $boardcast) 或者 基於 service 的方式都非常好用。轉戰 Backbone 之後,由於對 Backbone 的事件機制理解不夠且使用非常靈活,一直沒找到一個好的通信方式。直到看見這篇文章,作者通過一個簡單的例子,層層深入,把 Backbone View 之間通信的三種方式講的清晰明了。譯文如下:
我正在開發的這個網頁主要有兩部分,分別是 document 和 sidebar。
如上圖所示,我設立了三個視圖 (view) :
ApplicationView - 作為最外層視圖來包含下級視圖
DocumentView - 展示正在編輯或浏覽的內容
SidebarView - 展示一些和 document 相關的信息
DocumentView 和 SidebarView 作為 ApplicationView 的子視圖,所以整體的視圖結構如下圖所示:
用戶在任意一個子視圖進行操作,另一個子視圖都需要隨之變化。但由於兩個子視圖之間並不能直接通知對方(也就是說,它們的作用域沒有直接聯系,不像父視圖,可以包含它所有子視圖的作用域),所以,我需要一個事件機制。
在我谷歌和參考其他人的方法之後,我總結出了如下三種不同的通信方式。
1. 通過父視圖傳遞事件
我通過父視圖 ( ApplicationView ) 來為它的兩個子視圖傳遞事件。因為父視圖包含它所有子視圖的作用域,因此用它作為事件傳遞的媒介最好不過。
JavaScript 代碼如下:
var ApplicationView = Backbone.View.extend({ initialize : function(){ this.documentView = new DocumentView({parent:this}); this.sidebarView = new SidebarView({parent:this}); this.documentView.on('edit', this.documentEdited, this); }, documentEdited : function(){ // do some stuff this.sidebarView.trigger('documentEdit'); } }); var DocumentView = Backbone.View.extend({ onEdit : function(){ this.trigger('edit'); } }); var SidebarView = Backbone.View.extend({ initialize : function(){ this.on('documentEdit', this.onDocumentEdit, this); }, onDocumentEdit : function(){ // react to document edit. } });
但是,這種方法並不高效。因為我需要在 ApplicationView 中添加一個額外的事件處理函數 documentEdited() 。如果子視圖有一堆事件傳過來,則在父視圖中會不斷觸發事件處理函數,導致它不堪重負。
那麼來看看第二種方法。
2. 通過 EventBus 在視圖間通信
我通過繼承 Backbone.Events 來創建一個全局對象 EventBus 。把它注入到各個子視圖中,用來廣播事件。
JavaScript 代碼如下:
var ApplicationView = Backbone.View.extend({ initialize : function(){ this.eventBus = _.extend({}, Backbone.Events); this.documentView = new DocumentView({ eventBus : this.eventBus }); this.sidebarView = new SidebarView({ eventBus : this.eventBus }); }, }); var DocumentView = Backbone.View.extend({ initialize : function(options){ this.eventBus = options.eventBus; }, onEdit : function(){ this.eventBus.trigger('documentEdit'); } }); var SidebarView = Backbone.View.extend({ initialize : function(options){ this.eventBus = options.eventBus; this.eventBus.on('documentEdit', this.onDocumentEdit, this); }, onDocumentEdit : function(){ // react to document edit. } });
在這個方法中,我把 EventBus 作為一個全局對象用來注冊事件。如果我想在各個視圖之間通信,只需要在視圖中注入 EventBus ,就可以通過它方便地觸發或監聽事件了。
注意:如果你不想要創建全局對象,你仍然可以創建模塊 (module) 或視圖 (view) 級別的 EventBus 用來通信。
這個方法已經明顯優於第一種方法了。但是需要我們手動的在子視圖中引入 EventBus ,說明還有可以改進的空間,那麼,來看看第三種方法。
3. 直接用 Backbone 作為事件注冊機
在第二種方法中,我創建了一個單獨的 EventBus ,繼承自 Backbone.Events 。但最近我悟到 Backbone 對象本身就是一個混合了 Events 的對象,所以我直接用 Backbone 廣播事件,就無需單另創建的 EventBus 了。
而且 Backbone 對象可以直接調用,這樣我就不必在每個子視圖中手動注入它了。
JavaScript 代碼如下:
var ApplicationView = Backbone.View.extend({ initialize : function(){ this.documentView = new DocumentView(); this.sidebarView = new SidebarView(); }, }); var DocumentView = Backbone.View.extend({ onEdit : function(){ Backbone.trigger('documentEdit'); } }); var SidebarView = Backbone.View.extend({ initialize : function(options){ Backbone.on('documentEdit', this.onDocumentEdit, this); }, onDocumentEdit : function(){ // react to document edit. } });
總結
我最終在我的項目中使用了第三種方法。而且在我看來,雖然它直接依賴了全局的 Backbone 對象,但是用起來卻異常簡潔。