本文转自:http://yujianshenbing.iteye.com/
9. View (视图控制器)
我们在前面的章节中介绍了Model和Collection,它们都是用于数据管理和交互,在本章我们讨论如何使用视图(View)将这些数据渲染到界面,以及如何管理界面事件和逻辑。
Backbone中的视图提供了一组处理DOM事件、和渲染模型(或集合)数据的方法(在使用视图类之前,你必须先导入jQuery或Zepto)。
视图类提供的方法非常简单,我们一般都会在Backbone.View的基础上进行扩展,但即使是特别复杂的视图类,它也应该仅仅是做界面事件、和渲染逻辑相关的操作,数据管理应该交由Model和Collection来完成,而业务逻辑应该由其它的类来完成。
9.1 定义和创建视图
我们通过一段例子来说明如何创建View:
- <div title="列表" style="color:red" id="list" class="listview"></div>
- <script type="text/javascript">
- var ListView = Backbone.View.extend({
- el : '#list'
- });
- var listview = new ListView();
- </script>
在这个例子中,我们定义了一个ListView视图类,它继承自Backbone.View,我们通过new关键字实例化一个ListView对象。
在定义ListView时,我们设置了el属性,它应该是一个字符串形式的DOM选择器,但视图对象在实例化时,会在内部通过这个选择器获取对应的DOM对象,并重新存放在el属性中。因此我们可以在视图的内部通过this.el来访问所关联的DOM对象。
每个视图对象都会关联一个DOM 对象,视图中所有操作都限定在这个DOM对象之内,这样做可以便于视图界面的控制(如渲染、隐藏和移除等),同时能提高查找视图内子元素的效率。
上面的例子中,id为list的标签是我们事先准备好的,在定义ListView时可以直接通过#list选择器来引用它,但实际开发时这些DOM可能是动态生成的,至少在定义视图类时它们还不存在。此时,我们可以通过另一种方式来设置视图的DOM对象:
- <script type="text/javascript">
- var ListView = Backbone.View.extend({
- tagName : 'div',
- className : 'listview',
- id : 'list',
- attributes : {
- title : '列表',
- style : 'color:red'
- },
- render : function() {
- this.el.innerHTML = 'Hello World!';
- document.body.appendChild(this.el);
- }
- });
- var listview = new ListView();
- listview.render();
- </script>
运行这个例子,页面上输出了一段红色的文字:Hello World!。如果你通过Firebug等工具查看当前的DOM结构,你能看到</body>结束标签之前多了这样一段:
- <div title="列表" style="color:red" id="list" class="listview">Hello World!</div>
这段标签是视图对象在实例化时根据tagName、className、id和attributes属性自动创建的,结合我们的代码,你可以很清晰地看出:
- tagName表示新标签的名称(如果没有设置,则默认为div标签)
- className对应标签的class样式属性
- id对应标签的id属性
这3个是最常见的HTML属性,你可以在定义视图类时直接设置它们,如果还需要设置更多的属性,可以通过attributes属性来定义。
你可能注意到,我们还定义了一个render()方法,在创建ListView实例之后,我们调用该方法将新建的标签添加到页面尾部,否则它只会存储在el属性中,而不会被显示。
上面介绍的两种创建方式,是在两种不同的场景中使用,分别是对已经存在的DOM创建视图,和创建视图时同时创建新的DOM,因此你不应该不会同时使用到它们。
9.2 处理DOM事件
视图很重要的一个特性是帮助我们自动绑定界面事件。回想一下我们以前是如何为界面标签绑定事件的?可能就像这样:
- <p>
- <input type="button" value="Create" id="create" />
- <input type="button" value="Read" id="read" />
- <input type="button" value="Update" id="update" />
- <input type="button" value="Delete" id="delete" />
- </p>
- <script type="text/javascript">
- function createData() {
- // todo
- }
- function readData() {
- // todo
- }
- function updateData() {
- // todo
- }
- function deleteData() {
- // todo
- }
- $('#create').on('click', createData);
- $('#read').on('click', readData);
- $('#update').on('click', updateData);
- $('#delete').on('click', deleteData);
- </script>
这是一个典型的通过jQuery绑定DOM事件的例子,如果你正在开发或曾经开发过一些复杂的应用,你可能尝试过通过某种方式将这些代码更好的组织起来,以便使它们看起来结构更加清晰,更易维护。
Backbone的视图对象为我们提供了事件的自动绑定机制,用于更好地维护DOM和事件间的关系,来看看下面的例子:
- <p id="view">
- <input type="button" value="Create" id="create" />
- <input type="button" value="Read" id="read" />
- <input type="button" value="Update" id="update" />
- <input type="button" value="Delete" id="delete" />
- </p>
- <script type="text/javascript">
- var MyView = Backbone.View.extend({
- el : '#view',
- events : {
- 'click #create' : 'createData',
- 'click #read' : 'readData',
- 'click #update' : 'updateData',
- 'click #delete' : 'deleteData'
- },
- createData : function() {
- // todo
- },
- readData : function() {
- // todo
- },
- updateData : function() {
- // todo
- },
- deleteData : function() {
- // todo
- }
- });
- var view = new MyView();
- </script>
在这个例子中,我们将4个按钮放在一个id为view的标签中,并将这个标签与视图类MyView进行了关联。
在定义视图类时,我们声明了一个events属性,它表示视图中的用户事件列表,描述方式如下:
事件名称 选择器 : 事件处理函数
事件名称可以是DOM对象支持的任何事件,选择器可以是jQuery或Zepto支持的任意选择器字符串(包括标签选择器、类选择器、id选择器等),而事件处理函数应该是已经定义在视图类本身的方法名称。
视图对象会自动解析events列表中的描述,即使用jQuery或Zepto获取选择器描述的DOM对象,并将事件处理函数绑定到事件名称中。这些操作都会在视图类被实例化时自动完成,我们可以更关心视图类本身的结构,而不是刻意地去考虑如何绑定事件。
你可能在担心另外一个问题:如果视图的DOM结构是动态生成的,Backbone是否提供了相应的方法用于动态绑定和解除事件?
其实你并不需要关心这个问题,因为events中的事件是通过delegate()方法绑定到视图对象的el元素上,而并非是选择器所描述的元素。因此视图内的结构无论如何变化,events中的事件都是有效的。
(如果你对jQuery比较熟悉,可能了解它所提供的delegate()方法。该方法实际上将事件绑定在父层元素,然后在事件冒泡过程中,通过检查目标子元素来触发事件。)
视图对象通过delegate()方法绑定事件,意味着我们不需要关心视图结构变化对事件产生的影响,同时也说明events中选择器所对应的元素必须处于视图的el元素之内,否则绑定的事件是无法生效的。
尽管如此,有些情况下可能我们仍然需要手动绑定和解除事件,视图对象提供了delegateEvents()和undelegateEvents()方法用于动态绑定和解除events事件列表,你可以通过查看API文档来了解它们。
9.3 渲染视图和数据
我们之前提到,视图主要用于界面事件的绑定和数据渲染,然而视图对象仅仅提供了一个和渲染相关的方法render(),并且它是一个没有任何逻辑、也没有任何地方引用到的空方法,我们需要重载它来实现自己的渲染逻辑。
视图中可能包含许多界面逻辑,这里建议所有的视图子类都重载render()方法,并将它作为最终渲染的入口方法。在团队开发中,严格按照规范编码可以帮助别人更好地理解和维护你的代码。