只扯蛋,不给代码,就是耍流氓 -- honger。
完整的 tutorial 代码 戳这里, 因为我使用的是 commonjs 规范,基于 spm 的,你可以先安装,然后运行它。更多 spm 资料
// 安装
npm install spm -g
// 运行
spm server
这个 repo 是我学习各种技术栈的一个集合,如果是初学者,可以跟我一起来学习,也可以私信我。
你也可以 find me on GitHub
Backbone.Marionette 的中文资料真是少之又少,因此这篇会尽量介绍的较为详细。
很多人抱怨和吐槽 Backbone
, 觉得它太简单了。什么事都要自己做。然而,Backbone 的优点也是它太简单了,它的思想就是不作任何绑定, 只提供一个骨架。正如 Backbone 的中文意思。
所以大量的问题都留给开发者自己想办法来解决,因此遭到吐槽...
当然,使用纯 Backbone 开发一个复杂应用时,情况就会变得非常糟糕。
纯 Backbone 的工作流程是这样的: MVP
events commands
Template/DOM (View) ----------> Backbone.View (Presenter) ----------> Backbone.Model (Model)
| | | |
| updates | | events |
|<--------------------------| |<--------------------------------|
其中涉及的问题有:
业务逻辑
: model 和 collection 处理大部分逻辑。他们对应着服务端后台的资源,也对应着视图显示的类容。构建 DOM
:一般是 handlebars。视图逻辑
:Backbone.View ,其中的逻辑要自己维护。视图和模型同步
: 自己维护。管理复杂的 UI 交互
:自己维护。管理状态和路由
:Backbone.Router(不支持管理视图和应用状态)创建与连接组件
: 手动实现。
管理复杂的 UI 交互
那么,这篇文章着重于讲 UI 交互,所有的 UI 交互都可以被划分为:
简单交互
:使用观察者同步(Observer Synchronization),被动控制显示,操作 DOM 事件来改变集合模型,视图监听集合模型的变化来改变自身。Backbone.View 就是这样工作的。复杂交互
:使用流同步(Flow Synchronization),主动控制显示 SUPERVISING PRESENTERS
使用 Marionette 工作流程是这样的:
events for
complex interactions notice
Template/DOM (View) ----------------------> Marionette.View (Presenter) ------------> Backbone.Model (Model)
| | | |
| complex updates | | events |
|<------------------------------------------| |<-------------------------------|
| |
| simple updates |
|------------------------------->|
视图 View & 区域 Region
Marionette 扩展了非常丰富的视图 View 组件:ItemView
CollectionView
CompositeView
LayoutView
.
不仅于此,Marionette 还使用 Region (区域)来配合 View (视图)。
一般会先添加一个 Region 来定位一块地方,再决定这块地方显示哪个 View 。
// 你可以理解为一个中心 APP 对象。当一切准备就绪的时候,调用 App.start(options) 启动应用。
var MyApp = new Backbone.Marionette.Application();
// 添加一个 region,它对应一个 dom 节点
App.addRegions({
mainRegion: '#content'
});
// 让这个 region 显示一个视图, 这个视图会立即渲染
App.mainRegion.show(new MyView());
从头说起吧,你可能注意到了,上面实例化了一个 Marionette.Application 对象。
通常需要定义一个 App ,通过 Initializers 把所有的事都绑定在上面。等待 start 方法调用的时候,开始执行。
// start 方法调用后,立即执行 Initializers
MyApp.addInitializer(function(options) {
// 实例化 compositeView
var angryCatsView = new AngryCatsView({
collection: options.cats
});
// 显示这个视图
MyApp.mainRegion.show(angryCatsView);
})
MyApp.start({cats: cats});
这里只绑定了一个 Initializer,在一个复杂的应用中,你可能会绑定多个的。start(options) 中的参数 options 会传递个每个 Initializer。
通过扩展 Backbone.Events, 实现 Aspect(切面编程) 你可以监听这些事件,让应用更加灵活。
之前分析过 Arale 的 Events 代码,现在的 Backbone.Events 就是从 Arale 的 Events 合并过来的,看我的 gitbook
App.on('initialize:before', function(options) {
// doSomething...
});
App.on('initialize:after', function(options) {
// doOtherthing...
});
App.on('start', function(options) {
Backbone.history.start();
});
好了,现在已经知道了 Marionette 是怎么启动的了,下一步是了解它是怎么管理视图 View 的。
LayoutView
布局视图,比如你的界面上可能用 header main footer 等区域。
你可以这样来定义布局视图,这样你就掌控全局了。
var RootLayout = Backbone.Marionette.LayoutView.extend({
el: '#content',
regions: {
header: '#header',
main: '#main',
footer: '#footer'
}
})
一般会把这个 root 挂载到 App 上。
var MyApp = Backbone.Marionette.Application.extend({
setRootLayout: function () {
this.root = new RootLayout();
}
});
// App 启动前,实例化它,得到 App.root
MyApp.on('before:start', function () {
MyApp.setRootLayout();
});
现在 App 上有了 root 的控制权了,可以任意设置某个区域显示某个视图了。
MyApp.root.showChildView('header', new HeaderLayout());
ItemView 与 CompositeView
单条记录 Model 对应 ItemView, CompositeView 不仅对应的是一个包含 ItemView ,还对应有其他一些相关视图.
他们通常在一起使用,并且 ItemView 是 CompositeView 的 childView 属性值。
比如这样两个 template
Name
{{name}}
我需要在 tbody 标签下加入多个 ItemView。这样你就需要使用 childViewContainer 来指定 childView 被加在什么地方。
很显然此处: {childViewContainer: 'tbody'}
.
这里是以上代码
var AngryCatView = Backbone.Marionette.ItemView.extend({
template: require('./tpl/angrycat.handlebars'),
tagName: 'tr',
className: 'angry_cat'
});
var AngryCatsView = Backbone.Marionette.CompositeView.extend({
tagName: 'table',
id: 'angry_cats',
className: 'table-striped table-bordered',
template: require('./tpl/angrycats.handlebars'),
childView: AngryCatView,
childViewContainer: 'tbody'
})
细心的童鞋应该已经注意到了,模板中为什么不用 table 标签包裹起来。这是因为 Marionette 会为你包裹一层,若你不指定 tagName
则默认是 div 标签。 指定的 className
, id
属性也是加在这层上面的。
纯 Backbone 代码需要自己来实现 render 渲染 DOM,在这里 Marionette 通过指定的 template 属性自动渲染了。
重点 在 这一层 我们要做的是监听 Model,Collection 的变化,来同步视图。和 Backbone 做法一样。不同的是,Marionette 可以指定是全部重绘(render)还是部分重绘(renderCollection)
Event Aggregator
事件聚合器,这个东西主要是用来解耦的,例如:从 ItemView 上来个事件,改变了 model 的属性。影响了集合的排列。需要更新到 CompositeView 上。
在 ItemView 中调用 CompositeView ? 这样做是不对的,因为会让应用越来越复杂的。视图 View 也不应该去处理 business logic。
使用 Event Aggregator 会让程序解耦,它相当于一种 Publish/Subscribe 模式。视图只需要去通知 notice 模型 Model or Collection 来处理。
MyApp.trigger('rank:up', this.model);
在模型 Model or Collection 初始化的时候就要 Subscribe 订阅事件 rank:up
MyApp.on('rank:up', function(cat) {
if (cat.get('rank') === 1) {
return true;
}
self.rankUp(cat);
self.sort();
})
这样的话,business logic 就是在 Model or Collection 中维护的。
以上总结
Marionette.View 在 Backbone.View 之上多做了很多事情,包括自动渲染和重绘等等。
Marionette.View 接到 Dom 事件后,可以通知 notice 集合 Collection 去处理。也可以直接命令 commands 模型 Model 去处理。
(这里只介绍了一些入门知识 Marionette 未完待续...)