在上篇文章給大家介紹了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 stuffthis.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 對象,但是用起來卻異常簡潔。