掌握一个 MVC 框架,最关键的一节就是掌握如何在各个 View 之间通信。之前用 Angular 时,觉得基于事件的通信方式 ($on, $emit, $boardcast) 或者 基于 service 的方式都非常好用。转战 Backbone 之后,由于对 Backbone 的事件机制理解不够且使用非常灵活,一直没找到一个好的通信方式。直到看见这篇文章,作者通过一个简单的例子,层层深入,把 Backbone View 之间通信的三种方式讲的清晰明了。译文如下(已拿到授权):
如上图所示,我设立了三个视图 (view) :
ApplicationView
- 作为最外层视图来包含下级视图
DocumentView
- 展示正在编辑或浏览的内容
SidebarView
- 展示一些和 document 相关的信息
DocumentView
和 SidebarView
作为 ApplicationView
的子视图,所以整体的视图结构如下图所示:
用户在任意一个子视图进行操作,另一个子视图都需要随之变化。但由于两个子视图之间并不能直接通知对方(也就是说,它们的作用域没有直接联系,不像父视图,可以包含它所有子视图的作用域),所以,我需要一个事件机制。
1. 通过 EventBus 在视图间通信
EventBus
。把它注入到各个子视图中,用来广播事件 .
var ApplicationModel = Backbone.Model.extend({ }); var ApplicationView = Backbone.View.extend({ initialize: function () { console.log("--ApplicationView--"); var eventObj = new ApplicationModel()
var documentView = new DocumentView({ "eventObj":eventObj }); new SidebarView({ "eventObj":eventObj }); documentView.onEdit(); } }); var DocumentView = Backbone.View.extend({ initialize: function (option) { console.log("--DocumentView--"); this.eventObj = option.eventObj; }, onEdit:function(){ console.log("--onEdit--"); this.eventObj.trigger("edit"); } }); var SidebarView = Backbone.View.extend({ initialize: function (option) { console.log("--SidebarView--"); this.eventObj = option.eventObj; this.listenTo(this.eventObj,"edit",this.onDocumentEdit); }, onDocumentEdit:function(){ console.log("--onDocumentEdit--"); } }); $(document).ready(function () { new ApplicationView(); });
在这个方法中,我把 EventBus
作为一个全局对象用来注册事件。如果我想在各个视图之间通信,只需要在视图中注入EventBus
,就可以通过它方便地触发或监听事件了。
注意:如果你不想要创建全局对象,你仍然可以创建模块 (module) 或视图 (view) 级别的 EventBus
用来通信
2. 直接用 Backbone 作为事件注册机
但最近我悟到 Backbone
对象本身就是一个混合了 Events
的对象,所以我直接用 Backbone
广播事件,就无需单另创建的 EventBus
了。
而且 Backbone 对象可以直接调用,这样我就不必在每个子视图中手动注入它了。
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.
}
});