骨干区域骨干入门_骨干入门

骨干区域骨干入门

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,并处理在视图本身的一部分上触发的事件。

在本文中,了解Backbone.js框架的不同组件。 探索MVC如何应用于骨干网。 通过示例,了解在创建Ajax应用程序或单页界面(SPI)时Backbone有多么有用。

下载本文中使用的源代码。

SPI应用程序: Backbone.RouterBackbone.history

具有许多Ajax交互的应用程序变得越来越像没有页面刷新发生的应用程序。 这些应用程序经常尝试将交互限制为单个页面。 这种SPI方法可提高效率和速度,并且整个应用程序的响应速度更快。 状态的概念代替了页面的概念。 散列片段用于标识特定状态。 哈希片段是URL中哈希标记(#)之后的部分,并且是此类应用程序的关键元素。 清单1显示了使用两个不同哈希散列的SPI应用程序中的两个不同状态。

清单1. SPI或Ajax应用程序内的两个不同状态
http://www.example.com/#/state1
http://www.example.com/#/state2

骨干网提供了一个称为路由器的组件(在0.5版之前称为控制器)来路由客户端状态。 路由器扩展了Backbone.Router函数,并包含将状态与操作相关联的哈希映射( routes属性)。 当应用程序达到关联状态时,将触发特定操作。 清单2显示了一个骨干路由器的示例。

清单2. Backbone.Router示例:routers.js
App.Routers.Main = Backbone.Router.extend({
    
   // Hash maps for routes
   routes : {
      "" : "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类似于以下内容时,将调用五个操作( indexgetTeamsgetTeamsCountrygetTeamCountryfourOfour )。

  • http://www.example.com触发index()
  • http://www.example.com/#/teams触发getTeams()
  • http://www.example.com/#/teams/country1触发器getTeamsCountry()传递country1作为参数
  • http://www.example.com/#/teams/country1/team1触发器getTeamCountry()传递country1team1作为参数
  • http://www.example.com/#/something触发使用* (星号)的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.historyfragment属性。 它包含哈希片段的值。 此顺序有助于根据状态顺序管理浏览器历史记录。 要将用户定向到先前的状态,请单击浏览器的后退按钮。

清单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请求( POSTGETPUTDELETE )相关联。 首次保存模型对象时,将使用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 object
        console.log(data);
    }
});

另一个可用于集合的有趣方法是reset() ,它允许将多个模型设置到集合中。 reset()方法非常便于将数据引导到集合中(例如在页面加载时),以避免用户等待异步调用返回。

视图和客户端模板

Backbone中的视图与经典MVC方法中的视图不同。 Backbone视图扩展了Backbone.View函数并显示存储在模型中的数据。 视图提供了由el属性定义HTML元素。 可以通过组合tagNameclassNameid属性的值或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'
});

如果eltagNameclassNameid属性为空,则默认情况下为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.Team
    render : 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.Team
    render : function() {
        // Compile the template
        var 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应用程序时,骨干网可能是正确的解决方案。


翻译自: https://www.ibm.com/developerworks/opensource/library/wa-backbonejs/index.html

骨干区域骨干入门

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值