骨干区域骨干入门"/>
骨干区域骨干入门
骨干区域骨干入门
Web应用程序越来越多地使用客户端脚本和Ajax交互来关注前端。 随着JavaScript应用程序复杂性的增加,如果没有正确的工具和模式,编写高效,非重复和可维护JavaScript代码可能会面临挑战。 模型-视图-控制器(MVC)是服务器端开发中用于生成组织且易于维护的代码的常用模式。 MVC允许从表示层或页面的文档对象模型(DOM)分离数据(例如,在Ajax交互中经常使用JavaScript对象表示法(JSON)对象)或页面的文档对象模型(DOM),它也适用于客户端开发。
Backbone(也称为Backbone.js)是Jeremy Ashkenas创建的轻量级库,可用于创建类似MVC的应用程序。 骨干:
- 与Underscore.js(一个实用程序带库)具有硬依赖性
- 与jQuery / Zepto具有软依赖性
- 根据模型的变化自动更新应用程序HTML,有利于代码的可维护性
- 促进使用客户端模板,从而无需在JavaScript中嵌入HTML代码
模型,视图,集合和路由器是Backbone框架内的主要组件。 在Backbone中,模型存储通过RESTful JSON接口从服务器检索的数据。 模型与视图相关联,这些视图负责为特定的UI组件呈现HTML,并处理在视图本身的一部分上触发的事件。
常用缩写
- DOM:文档对象模型
- MVC:模型-视图-控制器
- SPI:单页接口
在本文中,了解Backbone.js框架的不同组件。 探索MVC如何应用于骨干网。 通过示例,了解在创建Ajax应用程序或单页界面(SPI)时Backbone有多么有用。
下载本文中使用的源代码。
SPI应用程序: Backbone.Router
和Backbone.history
具有许多Ajax交互的应用程序变得越来越像没有页面刷新发生的应用程序。 这些应用程序经常尝试将交互限制为单个页面。 这种SPI方法可提高效率和速度,并且整个应用程序的响应速度更快。 状态的概念代替了页面的概念。 散列片段用于标识特定状态。 哈希片段是URL中哈希标记(#)之后的部分,并且是此类应用程序的关键元素。 清单1显示了使用两个不同哈希散列的SPI应用程序中的两个不同状态。
清单1. SPI或Ajax应用程序内的两个不同状态
骨干网提供了一个称为路由器的组件(在0.5版之前称为控制器)来路由客户端状态。 路由器扩展了Backbone.Router
函数,并包含将状态与操作相关联的哈希映射( routes
属性)。 当应用程序达到关联状态时,将触发特定操作。 清单2显示了一个骨干路由器的示例。
清单2. Backbone.Router
示例:routers.js
App.Routers.Main = Backbone.Router.extend({// Hash maps for routesroutes : {"" : "index","/teams" : "getTeams","/teams/:country" : "getTeamsCountry","/teams/:country/:name : "getTeam""*error" : "fourOfour"},index: function(){// Homepage },getTeams: function() {// List all teams },getTeamsCountry: function(country) {// Get list of teams for specific country},getTeam: function(country, name) {// Get the teams for a specific country and with a specific name}, fourOfour: function(error) {// 404 page}
});
可以将创建的每个状态添加为书签。 URL类似于以下内容时,将调用五个操作( index
, getTeams
, getTeamsCountry
, getTeamCountry
和fourOfour
)。
-
触发
index()
-
触发
getTeams()
-
触发器
getTeamsCountry()
传递country1
作为参数 -
触发器
getTeamCountry()
传递country1
和team1
作为参数 -
触发使用
*
(星号)的fourOfour()
。
要启动Backbone,请在页面加载时实例化路由器,并通过Backbone.history.start()
方法指令监视哈希片段上的任何更改,如清单3所示 。
清单3.应用程序初始化(使用jQuery)
$(function(){var router = new App.Routers.Main();Backbone.history.start({pushState : true});
})
实例化路由器时会生成Backbone.history
对象; 它是对Backbone.History
功能的自动引用。 Backbone.history
负责将路由与router
对象中定义的操作进行匹配。 触发start()
方法后,将创建Backbone.history
的fragment
属性。 它包含哈希片段的值。 此顺序有助于根据状态顺序管理浏览器历史记录。 要将用户定向到先前的状态,请单击浏览器的后退按钮。
在清单3的示例中,使用启用HTML5功能pushState
的配置调用start()
方法。 对于支持pushState
的浏览器,Backbone将监视popstate事件以触发新状态。 如果浏览器不支持该HTML5功能,则会监视onhashchange
事件。 如果浏览器不支持此事件,则轮询技术将监视URL哈希片段上的任何更改。
型号和收藏
模型和集合是Backbone.js的重要组件。 模型将数据(通常来自服务器)保存为键值对。 要创建模型,请扩展Backbone.Model
,如清单4所示 。
清单4. Backbone.Model
创建
App.Models.Team = Backbone.Model.extend({defaults : {// default attributes}// Domain-specific methods go here
});
App.Models.Team
函数是一个新的模型函数,但是必须创建它的一个实例才能在应用程序中使用特定模型,如清单5所示 。
清单5.模型实例化
var team1 = new App.Models.Team();
变量team1
现在具有名为cid的字段,该字段是客户端标识符,形式为“ c”加一个数字(例如c0,c1,c2)。 模型由存储在哈希图中的属性定义。 可以在实例化时设置属性,也可以使用set()
方法设置属性。 可通过get()
方法检索属性值。 清单6显示了如何通过实例化或get()
/ set()
设置和获取属性。
清单6.模型实例化和get / set方法
// "name" attribute is set into the model
var team1 = new App.Models.Team({name : "name1"
});
console.log(team1.get("name")); // prints "name1"// "name" attribute is set with a new value
team1.set({name : "name2"
});
console.log(team1.get("name")); //prints "name2"
使用JavaScript对象时,使用set()
方法创建或设置属性值的原因可能并不明显。 原因之一是要更新值,如清单7所示 。
清单7.用错误的方式更新属性
team1.attributes.name = "name2";
避免使用清单7中的代码。 使用set()
是更改模型状态并触发模型更改事件的唯一方法。 使用set()
促进了封装原理。 下面的清单8显示了如何将事件处理程序绑定到change事件。 事件处理程序包含一个警报,该警报在调用set()
方法时触发,如清单6所示 ,但不是使用清单7中的代码触发的。
清单8. App.Models.Team模型中的更改事件处理程序
App.Models.Team = Backbone.Model.extend({initialize : function(){this.bind("change", this.changed);},changed : function(){alert("changed");}
});
Backbone的另一个好处是易于通过Ajax交互与服务器进行通信。 在模型上调用save()
方法将通过REST JSON API将当前状态(由属性的哈希图表征)异步保存到服务器。 清单9显示了一个示例。
清单9.在模型对象上调用的save
方法
barca.save();
在后台, save()
函数委托给Backbone.sync
,后者是负责发出RESTful请求的组件,默认情况下使用jQuery函数$.ajax()
。 因为涉及REST样式体系结构,所以每个创建,读取,更新或删除(CRUD)操作都与不同类型的HTTP请求( POST
, GET
, PUT
, DELETE
)相关联。 首次保存模型对象时,将使用POST
请求并创建标识符ID。 对于后续尝试将对象发送到服务器,将使用PUT
请求。
当需要从服务器检索模型时,将请求“读取”操作并使用Ajax GET
请求。 这种类型的请求使用fetch()
方法。 要确定服务器在其中推入或拉出模型数据的位置,请执行以下操作:
- 如果模型属于集合 ,则集合对象的
url
属性将是位置的基础,并且将附加模型ID(而非cid)以完成完整的URL - 如果模型不在集合内,
urlroot
模型的urlroot
属性用作位置的基础
清单10显示了如何获取模型。
清单10.模型对象的Fetch()
方法
var teamNew = new App.Models.Team({urlRoot : '/specialTeams'
});
teamNew.save(); // returns model's ID equal to '222'
teamNew.fetch(); // Ajax request to '/specialTeams/222'
validate()
方法可用于验证模型,如清单11所示 。 需要重写validate()
方法,该方法在调用set()
方法时触发,以包含模型的验证逻辑。 传递给此函数的唯一参数是一个JavaScript对象,其中包含由set()
方法更新的属性,因此可以验证这些属性的条件。 如果validate()
方法未返回任何内容,则验证成功。 如果返回错误消息,则验证失败,并且set()
方法将不会执行。
清单11.模型的验证方法
App.Models.Team = Backbone.Model.extend({validate : function(attributes){if (!!attributes && attributes.name === "teamX") {// Error message returned if the value of the "name" // attribute is equal to "teamX"return "Error!";}}
}
模型集被分组为扩展功能Backbone.Collection
集合。 集合的特征在于模型属性,该属性定义组成集合的模型的类型。 使用add()
/ remove()
方法向集合中添加和删除模型。 清单12显示了如何创建和填充集合。
清单12.骨干集合
App.Collections.Teams = Backbone.Collection.extend({model : App.Models.Team
});
var teams = new App.Collections.Teams();// Add e model to the collection object "teams"
teams.add(team1);
teams.add(new App.Models.Team({name : "Team B"
}));
teams.add(new App.Models.Team());
teams.remove(team1);console.log(teams.length) // prints 2
创建的teams
集合包含存储在models属性中的两个模型的数组。 但是,在典型的Ajax应用程序中,将从服务器动态填充(而不是手动添加)集合。 fetch()
方法有助于完成任务,如清单13所示 ,并将数据存储到模型数组中。
清单13. Fetch()
方法
teams.fetch();
Backbone中的集合具有url
属性,该属性定义服务器上的位置,使用Ajax GET请求从该位置提取JSON数据,如清单14所示 。
清单14.集合的url
属性和fetch()
方法
teams.url = '/getTeams';
teams.fetch(); //Ajax GET Request to '/getTeams'
Fetch()
方法是一个异步调用,因此在等待服务器响应时应用程序不会挂起。 在某些情况下,要操纵从服务器返回的原始数据,可以使用集合的parse()
方法,如清单15所示 。
清单15. parse()
方法
App.Collections.Teams = Backbone.Collection.extend({model : App.Models.Team,parse : function(data) {// 'data' contains the raw JSON objectconsole.log(data);}
});
另一个可用于集合的有趣方法是reset()
,它允许将多个模型设置到集合中。 reset()
方法非常便于将数据引导到集合中(例如在页面加载时),以避免用户等待异步调用返回。
视图和客户端模板
Backbone中的视图与经典MVC方法中的视图不同。 Backbone视图扩展了Backbone.View
函数并显示存储在模型中的数据。 视图提供了由el
属性定义HTML元素。 可以通过组合tagName
, className
和id
属性的值或el
本身的值来创建此属性。 清单16显示了两个不同的视图,它们以不同的方式构成el
属性。
清单16.骨干视图样本
// In the following view, el value is 'UL.team-element'
App.Views.Teams = Backbone.View.extend({el : 'UL.team-list'
});
// In the following view, el value is 'div.team-element'
App.Views.Team = Backbone.View.extend({className : '.team-element',tagName : 'div'
});
如果el
, tagName
, className
和id
属性为空,则默认情况下为el
分配一个空的DIV。
如前所述,视图必须与模型关联。 模型属性派上用场,如清单17所示 。 App.View.Team
视图与App.Models.Team
模型的实例绑定在一起。
清单17. Backbone视图中的Model属性
// In the following view, el value is 'UL.team-element'
App.Views.Team = Backbone.View.extend({...model : new App.Models.Team
});
要渲染数据(这是视图的主要目的render()
,请使用用于在el
属性所引用的DOM元素内显示模型属性的逻辑覆盖render()
方法。 清单18显示了render方法如何更新用户界面的示例。
清单18. Render()
方法
App.Views.Team = Backbone.View.extend({className : '.team-element',tagName : 'div',model : new App.Models.Teamrender : function() {// Render the 'name' attribute of the model associated// inside the DOM element referred by 'el'$(this.el).html("<span>" + this.model.get("name") + "</span>");}
});
Backbone还促进了客户端模板的使用,使不必要在JavaScript中嵌入HTML代码,如清单18所示 。 (使用模板,模板封装了视图之间通用的功能;仅指定一次该功能。)Backbone在underscore.js(必需的库)中附带有模板引擎,尽管不必使用此模板引擎。 清单19中的示例使用underscore.js HTML模板。
清单19.包含模板HTML
<script id="teamTemplate" type="text/template"><%= name %>
</script>
清单20显示了另一个使用underscore.js HTML模板的示例。
清单20.使用_.template()
函数的视图
App.Views.Team = Backbone.View.extend({className : '.team-element',tagName : 'div',model : new App.Models.Teamrender : function() {// Compile the templatevar compiledTemplate = _.template($('#teamTemplate').html());// Model attributes loaded into the template. Template is// appended to the DOM element referred by the el attribute$(this.el).html(compiledTemplate(this.model.toJSON()));}
});
Backbone中最有用和有趣的功能之一就是能够将render()
方法绑定到模型的change事件,如清单21所示 。
清单21.绑定到模型的change事件的Render()
方法
// In the following view, el value is 'div.team-element'
App.Views.Team = Backbone.View.extend({model : new App.Models.Team,initialize : function() {this.model.bind("change", this.render, this);}
})
该代码将render()
方法绑定到模型的change事件。 模型更改后,将自动触发render()
方法,从而节省了很多行代码。 从Backbone 0.5.2开始, bind()
方法接受第三个参数来定义回调函数的对象。 (在前面的示例中,当前视图将是回调render()
内的对象)。 在Backbone 0.5.2之前,必须利用underscore.js中的bindAll
函数,如清单22所示 。
清单22. _.bindAll()
用法
// In the following view, el value is 'div.team-element'
App.Views.Team = Backbone.View.extend({initialize : function() {_.bindAll(this, "render");this.model.bind("change", this.render);}
})
在骨干视图中,很容易听取视图中DOM元素引发的事件。 events
属性对于实现这一点变得非常方便,如清单23所示 。
清单23. Events属性
App.Views.Team = Backbone.View.extend({className : '.team-element',tagName : 'div',events : {"click a.more" : "moreInfo"},moreInfo : function(e){// Logic here}
})
events
属性中的每个项目都有两个部分:
- 左侧部分指示事件类型和触发事件的选择器。
- 右侧部分定义了事件处理函数。
在清单23中 ,当用户在具有类team-element
的DIV中单击具有more
类的链接时,将调用moreInfo
函数。
结论
MVC模式可以提供大型JavaScript应用程序所需的组织代码。 Backbone是一个JavaScript MVC框架,它轻巧且学习曲线很小。 模型,视图,集合和路由器将应用程序划分为不同的层,并负责一些特定的事项。 在处理Ajax应用程序或SPI应用程序时,骨干网可能是正确的解决方案。
翻译自: .html
骨干区域骨干入门
更多推荐
骨干区域骨干入门
发布评论