Backbone 是一个 JavaScript 库,可用于创建模型-视图-控制器 (model-view-controller, MVC) 类应用程序和单页界面。

Backbone实现一种清晰的MVC代码结构,解决了数据模型和视图映射的问题。运用Backbone最适合场景:构建一个大型或复杂的单页Web应用,需要用JS生成大量的页面内容(HTML为主),用户跟页面元素有很多的交互行为。

Backbone 强依赖于 Underscore(*必须),jQuery 是可选的,不过为了操作 DOM 方便一般都会引用到 jQuery。

Backbone框架源文件: http://www.whoishostingthis.com/resources/backbone-js/

Underscore框架的源文件: http://underscorejs.org/

我们也可以采用第三方cdn文件,如下:

<script src="http://libs.baidu.com/jquery/1.9.0/jquery.js"></script>
<script src="http://libs.baidu.com/json/json2/json2.js"></script>
<script src="http://libs.baidu.com/underscore/1.3.3/underscore-min.js"></script>
<script src="http://libs.baidu.com/backbone/0.9.2/backbone-min.js"></script>

学习Backbone我们需要学习几个重要函数:Model / Collection / View / Event / Router / History 其实Router和History是针对Web应用程序的优化 ,入门阶段可以只了解Model/Collection/View。将Model视为核心,Collection是Model的集合,View是为了实现Model的变化在前端的显示。

一 、 Model

创建Backbone.js的Model:

Person = Backbone.Model.extend({
    initialize: function(){      //初始化  在实例化时自动调用
        alert("hello backbonejs");
    }
});
var person = new Person;

设置属性:

Person = Backbone.Model.extend({ 
    initialize: function(){
        alert("hello backbonejs");
    }
});
var person = new Person({ name: "Lilei", age: 17 });
delete person;
var person = new Person();
person.set({ name: "Lilei", age: 17 })

获取属性:

Person = Backbone.Model.extend({
    initialize: function(){
        alert("hello backbonejs");
    }
});
var person = new Person({ name: "Thomas", age: 57, children: ['Tim'] });
var age = person.get("age");            // 57
var name = person.get("name");          // "Thomas"
var children = person.get("children");  // ['Tim']

设置默认属性:

Backbone.js提供了default属性用于对Model对象的默认值设置:

Person = Backbone.Model.extend({
    defaults: {
        name: 'Dear',
        age: 0,
        children: []
    },
    initialize: function(){
        alert("hello backbonejs");
    }
});
var person = new Person({ name: "Thomas", age: 57, children: ['Tim'] });
var age = person.get("age");            // 57
var name = person.get("name");          // "Thomas"
var children = person.get("children");  // ['Tim']

操作Model的属性:

我们可以在 Model添加任意数量的自定义方法。默认情况下,这些自定义添加的方法是public方法,能够在闭包外面被调用。

Person = Backbone.Model.extend({
    defaults: {
        name: 'Dear',
        age: 0,
        children: []
    },
    initialize: function(){
        alert("hello backbonejs");
    },
    adopt: function( newChildsName ) {
        var children_array = this.get("children");
        children_array.push( newChildsName );
        this.set({ children: children_array });
    }
});
var person = new Person({ name: "Thomas", age: 57, children: ['Tim'] });
person.adopt('John');
var children = person.get("children"); // ['Tim', 'John']

监听Model对象的变化:

Model的每个属性都可以有相应的监听函数,即当值发生变化时监听函 数就会被调用。在下面的代码中,在初始化函数中添加了对于Person的属性的监听函数。于是Person的对象的属性发生变化时,监听函数会立即被调用。

Person = Backbone.Model.extend({
    defaults: {
        name: 'Dear',
        age: 0,
        children: []
    },
    initialize: function(){
        alert("hello backbonejs");
        this.bind("change:name", function() {
            var name = this.get("name");  // 'Tom'
            alert("Changed my name to " + name);
        });
    },
    replaceNameAttr: function( name ) {
        this.set({ name:name });
    }
});
var person = new Person({ name: "Thomas", age: 57, children: ['Tim'] });
person.replaceNameAttr('Tom');

这里我们监听了Person的name属性,同样也可以监听整个对象所有属性的变化。

Model对象的其他方法(由于Model对象的方法较多,不再通过代码进行详细列举)

获取:除了前面提到的get()方法外,escape()方法先将数据中包含的HTML字符转换为实体形式(例如它会将双引号转换为"形式)再返回,用于避免XSS攻击。

修改:在原有数据的情况下使用set()方法

删除:

unset()方法用于删除对象中指定的属性和数据

clear()方法用于删除模型中所有的属性和数据

此外,Backbone中,模型对象提供了3个方法用于和服务器保持数据同步

save()方法:在服务器创建或修改数据

fetch()方法:从服务器获取数据

destroy()方法:从服务器移除数据

二、 View

Backbone.js中的Views是用来呈现Web应用程序中的数据模型,并完成对于事件的监听以及对事件的响应。

创建Backbone.js中的view:

SearchView = Backbone.View.extend({
    initialize: function(){
        alert("This is the View.");
    }
});
 
// initialize()会在View被初始化时立即执行
// 换句话说,这里的initialize()可以被看成构造函数
var search_view = new SearchView;

“el”属性:

“el”属性用于存储浏览器中原生态的DOM对象。每个Backbone的View对象都会具有el属性,如果不存在则Backbone.js会自动生成一个空的Div元素赋值给el。 这里我们将View的“el”属性设置为#search_container值,就相当于将Backbone.View设置为了这个DOM元素的操控者。

<div id="search_container"></div>
 
<script type="text/javascript">
    SearchView = Backbone.View.extend({
        initialize: function(){
            alert("This is the View.");
        }
    });
    var search_view = new SearchView({ el: $("#search_container") });
</script>
说明:所有在View内的事件触发都必须在View的el所指向的元素上。

加载模板

Backbone.js是基于Underscore.js,而Underscore里面包含了微型模板组建。下面的“render()”中,当View被初始化的时候会自动调用“render()”。然后,在render()中将我们模板中的内容加载到View的“el”元素中去。

_.template模板函数只能解析3种模板标签:

<% %>:用于包含JavaScript代码,这些代码将在渲染数据时被执行。

<%= %>:用于输出数据,可以是一个变量、某个对象的属性、或函数调用(将输出函数的返回值)。

<%- %>:用于输出数据,同时会将数据中包含的HTML字符转换为实体形式(例如它会将双引号转换为"形式),用于避免XSS攻击。

监听事件

如果需要在我们的View中添加事件绑定,我们就需要在Backbone.View中添加events属性。另外,我们需要强调事件绑定只适用View的el元素的子元素。下面我们对我们例子中的按钮添加事件绑定。

<div id="search_container"></div>
 
<script type="text/javascript">
    SearchView = Backbone.View.extend({
        initialize: function(){
            this.render();
        },
        render: function(){
            var template = _.template( $("#search_template").html(), {} );
            this.el.html( template );
        },
        events: {
            "click input[type=button]": "doSearch"     //单击 符合type=button的input元素时 执行视图中的doSearch方法
        },
        doSearch: function( event ){
            //按钮被点击后,你可以使用event.currentTarget获得被点击的元素
            alert( "Search for " + $("#search_input").val() );
        }
    });
 
    var search_view = new SearchView({ el: $("#search_container") });
</script>
 //模板
<script type="text/template" id="search_template">  
    <label>Search</label>
    <input type="text" id="search_input" />
    <input type="button" id="search_button" value="Search" />
</script>

这里需要说明的是events中的事件是通过delegate()方法绑定到视图对象的el元素上,而并非是选择器所描述的元素。因此视图内的结构无论如何变化,events中的事件都是有效的。

关于在模板中使用变量

<div id="search_container"></div>
 
<script type="text/javascript">
     SearchView = Backbone.View.extend({
        initialize: function(){
            this.render();
        },
        render: function(){
            // 设定传输给模板的变量
            var variables = { search_label: "My Search" };
            // 用underscore中_.template()渲染模板以及变量
            var template = _.template( $("#search_template").html(), variables );
            // 将渲染好的内容设置到View的el元素中
            this.el.html( template );
        },
        events: {
            "click input[type=button]": "doSearch"
        },
        doSearch: function( event ){
            // 按钮被点击后,你可以使用event.currentTarget获得被点击的元素
            alert( "Search for " + $("#search_input").val() );
        }
    });
 
    var search_view = new SearchView({ el: $("#search_container") });
</script>
 
<script type="text/template" id="search_template">
    //用<%= %>调用传输给模板的变量
    <label><%= search_label %></label>
    <input type="text" id="search_input" />
    <input type="button" id="search_button" value="Search" />
</script>

三、 Router

Backbone.js中的Router类简介

Backbone.Router担任了一部分Controller(控制器)的工作,它一般运行在单页应用中,能将特定的URL或锚点规则绑定到一个指定的方法(称为Action)。 当我们开发一个单页应用时,常常会遇到这样两个问题:

1.我们在同一个页面中通过用户的操作来隐藏、显示块内容,为用户提供一个无刷新、流 畅的体验,但用户希望通过浏览器的“前进”和“后退”按钮来返回和前进到上一步操作。而当他这样操作时, 会离开当前页面

2.用户在单页应用中操作,当他收藏一个页面时,他可能会将URL收藏起来或复制分享。但当他下一次重新打开链接,看到的却是初始化状态,而并不是当初的页面。

我们从例子中说明


 <script>
    var AppRouter = Backbone.Router.extend({   //路由的创建与前面的Model 和View 类似
        routes: {
            "*actions": "defaultRoute" // 对应于http://example.com/#anything-here
        },
        defaultRoute: function( actions ){
            // alert所有Route的action
            alert( actions );
        }
    });
    // 初始化Router
    var app_router = new AppRouter;
    // 开启Backbone的历史功能
    Backbone.history.start();
 
</script>
  

动态路由

大多现存的框架都能够支持你定义包含静态内容以及动态参数的路由。例如你需要通过文章id获取到文章的链接,很常见的例子就是“http://example.com/#/posts/12”。当链接被访问时,你希望能够获取到id值。下面就用程序实现了我们的要求:

<script>
    var AppRouter = Backbone.Router.extend({
        routes: {
            "/posts/:id": "getPost",
            "*actions": "defaultRoute" // Backbone会按照从上到下的顺序对Route进行匹配
        },
        getPost: function( id ) {
            // 在Route中定义的变量,可以传入到Route对应的处理函数
            alert( "Get post number " + id );
        },
        defaultRoute: function( actions ){
            alert( actions );
        }
    });
    // 初始化Router
    var app_router = new AppRouter;
    // 开启Backbone的历史功能
    Backbone.history.start();
 
</script>

动态路由(”:params”和”*splats”)

在Backbone的Route中,我们有两种方式传递Route中的变量。第一种:“:params”用于匹配URL中用“/”隔开的一部分;第 二种:“*splats”用于匹配从前面的字串的下一个字符开始直至URL结尾。换句话说,用“*splat”匹配到的变量总是URL中最后一个变量。任何在Route中定义的变量最终都会以变量的形式传递到用户所绑定函数。例如,“/:route/:action”会传递两个变量给处理这一URL的函数(“route”和“action”)。

routes: {
    "/posts/:id": "getPost",
    // 如:<a href="http://example.com/#/posts/121">例子</a>
 
    "/download/*path": "downloadFile",
    // 如:<a href="http://example.com/#/download/user/images/loading.gif">下载</a>
 
    "/:route/:action": "loadView",
    // 如:<a href="http://example.com/#/v2/aboutus">加载内容</a>
},
getPost: function( id ){
    alert(id); // 121
},
downloadFile: function( path ){
    alert(path); // user/images/loading.gif
},
loadView: function( route, action ){
    alert(route + "_" + action); // v2_aboutus
}

四、Collection

简单地说,Backbone的Collection就是一组Model的集合。下面是几个使用场景:

Model: Student, Collection: ClassStudents
Model: Todo Item, Collection: Todo List
Model: Animals, Collection: Zoo

声明一点,在Collection中只能存放单一类型的Model,但是同一种类型的Model可以在不同的Collection中出现:

Model: Student, Collection: Gym Class
Model: Student, Collection: Art Class
Model: Student, Collection: English Class

下面是一个常见的例子:

var Song = Backbone.Model.extend({
    initialize: function(){
        console.log("Music is the answer");
    }
});
 
var Album = Backbone.Collection.extend({
    model: Song
});

Collection的创建

var Song = Backbone.Model.extend({
    defaults: {
        name: "Not specified",
        artist: "Not specified"
    },
    initialize: function(){
        console.log("Music is the answer");
    }
});
 
var Album = Backbone.Collection.extend({
    model: Song
});
 
var song1 = new Song({ name: "How Bizarre", artist: "OMC" });
var song2 = new Song({ name: "Sexual Healing", artist: "Marvin Gaye" });
var song3 = new Song({ name: "Talk It Over In Bed", artist: "OMC" });
 
var myAlbum = new Album([ song1, song2, song3]);
console.log( myAlbum.models ); // [song1, song2, song3]

DEMO:

通过以上模块的学习,我们对backbone的基础有了初步的了解,以下列举几个简单的实际案例,自己可以通过源代码进行阅读学习,进一步巩固知识。

demo1: 待更新