实例讲解JavaScript的Backbone.js框架中的View视图
Backbone中的View用来反映你app中Model的模样。它们会监听事件并作出相应的反应。
接下来的教程我不会告诉你如何把Model和Collection绑定到View上,而是主要讨论View是如何使用javascript模板库的,尤其是Underscore.js's_.template。
这里我们使用jQuery来操作DOM元素,当然你也可以使用其他的库,例如MooTools或者Sizzle,但是Backbone的官方文档推荐我们使用jQuery。
接下来,我们以搜索框为例来新建一个View:
SearchView=Backbone.View.extend({
initialize:function(){
alert("WelcometoBackbone!");
}
});
varsearch_view=newSearchView();
无论是Model,View还是Collection,当被实例化时,initialize()方法都会被自动触发。
el属性
el属性指的是已经在浏览器中创建好的DOM对象,每个View都有一个el属性,如果它未被定义,Backbone将会自己创建一个空的div元素作为el属性。
下面让我们来为View创建一个el属性,并设为#search_containe。
<divid="search_container"></div>
<scripttype="text/javascript">
SearchView=Backbone.View.extend({
initialize:function(){
alert("WelcometoBackbone!");
}
});
varsearch_view=newSearchView({el:$("#search_container")});
</script>
此时,View的el属性指的是id为search_container的div元素。我们此时变绑定了这个div元素,那么任何我们希望触发的事件都必须在这个div元素中。
加载模板
Backbone是强依赖于Underscore.js的,所以我们可以使用Underscore.js中的小型模板。
现在,让我们添加一个render()方法,并且在initialize()中调用它,这样当View初始化时便会自动触发render()方法。
这个render()方法将会通过jQuery把模板加载到View的el属性中去。
<scripttype="text/template"id="search_template">
<label>Search</label>
<inputtype="text"id="search_input"/>
<inputtype="button"id="search_button"value="Search"/>
</script>
<divid="search_container"></div>
<scripttype="text/javascript">
SearchView=Backbone.View.extend({
initialize:function(){
this.render();
},
render:function(){
//通过Underscore编译生成模板
vartemplate=_.template($("#search_template").html(),{});
//讲生成的模板加载到el属性中
this.$el.html(template);
}
});
varsearch_view=newSearchView({el:$("#search_container")});
</script>
添加监听事件
我们使用View的events属性添加监听事件,记住监听事件只能添加到el属性的子元素上。现在,我们来给子元素button添加一个监听事件。
<scripttype="text/template"id="search_template">
<label>Search</label>
<inputtype="text"id="search_input"/>
<inputtype="button"id="search_button"value="Search"/>
</script>
<divid="search_container"></div>
<scripttype="text/javascript">
SearchView=Backbone.View.extend({
initialize:function(){
this.render();
},
render:function(){
vartemplate=_.template($("#search_template").html(),{});
this.$el.html(template);
},
events:{
"clickinput[type=button]":"doSearch"
},
doSearch:function(event){
//当button被点击时触发alert
alert("Searchfor"+$("#search_input").val());
}
});
varsearch_view=newSearchView({el:$("#search_container")});
</script>
向模板中传递参数
模板可以通过<%=%>的形式使用从View中传来的参数。
<scripttype="text/template"id="search_template">
<!--通过<%=%>形式使用传来的参数-->
<label><%=search_label%></label>
<inputtype="text"id="search_input"/>
<inputtype="button"id="search_button"value="Search"/>
</script>
<divid="search_container"></div>
<scripttype="text/javascript">
SearchView=Backbone.View.extend({
initialize:function(){
this.render();
},
render:function(){
//定义需要传递的参数
varvariables={search_label:"MySearch"};
//通过Underscore生成模板,同时传递参数
vartemplate=_.template($("#search_template").html(),variables);
//LoadthecompiledHTMLintotheBackbone"el"
this.$el.html(template);
},
events:{
"clickinput[type=button]":"doSearch"
},
doSearch:function(event){
alert("Searchfor"+$("#search_input").val());
}
});
varsearch_view=newSearchView({el:$("#search_container")});
</script>
处理DOM事件
视图很重要的一个特性是帮助我们自动绑定界面事件。回想一下我们以前是如何为界面标签绑定事件的?可能就像这样:
<p>
<inputtype="button"value="Create"id="create"/>
<inputtype="button"value="Read"id="read"/>
<inputtype="button"value="Update"id="update"/>
<inputtype="button"value="Delete"id="delete"/>
</p>
<scripttype="text/javascript">
functioncreateData(){
//todo
}
functionreadData(){
//todo
}
functionupdateData(){
//todo
}
functiondeleteData(){
//todo
}
$('#create').on('click',createData);
$('#read').on('click',readData);
$('#update').on('click',updateData);
$('#delete').on('click',deleteData);
</script>
这是一个典型的通过jQuery绑定DOM事件的例子,如果你正在开发或曾经开发过一些复杂的应用,你可能尝试过通过某种方式将这些代码更好的组织起来,以便使它们看起来结构更加清晰,更易维护。
Backbone的视图对象为我们提供了事件的自动绑定机制,用于更好地维护DOM和事件间的关系,来看看下面的例子:
<pid="view">
<inputtype="button"value="Create"id="create"/>
<inputtype="button"value="Read"id="read"/>
<inputtype="button"value="Update"id="update"/>
<inputtype="button"value="Delete"id="delete"/>
</p>
<scripttype="text/javascript">
varMyView=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
}
});
varview=newMyView();
</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文档来了解它们。
渲染视图和数据
视图主要用于界面事件的绑定和数据渲染,然而视图对象仅仅提供了一个和渲染相关的方法render(),并且它是一个没有任何逻辑、也没有任何地方引用到的空方法,我们需要重载它来实现自己的渲染逻辑。
视图中可能包含许多界面逻辑,这里建议所有的视图子类都重载render()方法,并将它作为最终渲染的入口方法。在团队开发中,严格按照规范编码可以帮助别人更好地理解和维护你的代码。