Backbone.js教程(初级篇) 这是菜鸟与老油条,屌丝与高富帅的战争 —— 译者:茶几 @chajn 原作者:脱袜子·戴蕾丝 原文:http://backbonetutorials.com/

来源:互联网 发布:交友网站程序源码 编辑:程序博客网 时间:2024/05/17 07:49

Backbone.js教程(初级篇)

这是菜鸟与老油条,屌丝与高富帅的战争 —— 译者:茶几 @chajn

原作者:脱袜子·戴蕾丝  原文:http://backbonetutorials.com/

1. 为啥你需要Backbone.js?

我们知道,仅仅使用jQuery或MooTools啥的来构建web应用或复杂的用户界面是极其困难的。
主要是因为这些JS库都把心思花费在它们擅长做的事情上了,而并没有意识到的是,即便没有任何页面结构,也是可以构建完整的应用的。
用Backbone就可以很容易的把你的应用部署到一个通过jQuery回调的嵌套堆中,然后在页面生成DOM元素实体。

我不需要解释为什么没有结构的页面构建是一个坏主意。
你当然可以每次构造应用的时候都发明一种实现方式,但这只是瞬间的美丽,你错过的是后世的精彩!
那你将永远只能是个屌ser。Balabala……

2. 啥是model?

网络上对MVC的定义是比较尿的,你甚至不知道怎样去理解和怎样去做。
但backbone.js的作者对backbone.js的model表现的定义就很明确。
Models是任何JS应用的核心部分,包括数据交互和众多相关逻辑:格式转换,校验,属性计算,访问控制等。
好的,让我们先创建一个model:

?
1
2
3
4
5
6
7
Person = Backbone.Model.extend({
        initialize: function(){
            alert("Welcome to this world");
        }
    });
    
    var person = new Person;

-
SO我们看到new一个model的实例后就会触发initialize()函数(models,collections 和 views 的工作机制都是一样滴)。虽然可以不声明initialize,但我们可能会经常用到。

2.1 设置属性

现在我们想设置一些属性,有两种方式,可以在创建model实例时进行传参,也可以在实例生成后通过model.set(obj)来进行设置。  

?
01
02
03
04
05
06
07
08
09
10
11
Person = Backbone.Model.extend({
        initialize: function(){
            alert("Welcome to this world");
        }
    });
    
    var person = new Person({ name: "Thomas", age: 67});
    delete person;
    //或者用set,操作等价
    var person = new Person();
    person.set({ name: "Thomas", age: 67});

-
两种方式在功能上是一样的。有设置属性,就有读取属性,下一步我们来获取它们。

2.2 获取属性

很简单,用 model.get(name)方法就可以读取属性值了。

?
01
02
03
04
05
06
07
08
09
10
11
Person = Backbone.Model.extend({
        initialize: function(){
            alert("欢迎来到这个二蛋的世界");
        }
    });
    
    var person = new Person({ name: "Thomas", age: 67, children: ['Ryan']});
    
    var age = person.get("age"); // 67
    var name = person.get("name"); // "Thomas"
    var children = person.get("children"); // ['Ryan']

-

2.3 设置model默认属性

有的时候你可能会想让model有默认属性值。
没的问题,只要在进行model声明的时候设置个'defaults'就行了。

?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
Person = Backbone.Model.extend({
        defaults: {
            name: 'Fetus',
            age: 0,
            children: []
        },
        initialize: function(){
            alert("欢迎来到这个三蛋的世界");
        }
    });
    
    var person = new Person({ name: "Thomas", age: 67, children: ['Ryan']});
    
    var age = person.get("age"); // 67
    var name = person.get("name"); // "Thomas"
    var children = person.get("children"); // ['Ryan']

-

2.4 操纵model的属性

Models可以添加自定义方法来进行自身的属性修改,默认这些方法都是公开的。

?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
Person = Backbone.Model.extend({
        defaults: {
            name: 'Fetus',
            age: 0,
            children: []
        },
        initialize: function(){
            alert("欢迎来到这个四蛋的世界");
        },
        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: 67, children: ['Ryan']});
    person.adopt('John Resig');
    var children = person.get("children"); // ['Ryan', 'John Resig']

-
添加了自定义方法之后,我们就可以更好的控制实例的自身属性了。

2.5 监听model的属性改变

现在说点有用的,我们可以通过model.bind(event,callback)方法来绑定change事件来监听属性改变。
下面的这个例子就是在initialize方法中绑定了一个name属性改变的事件监听。
如果person的name属性改变了,就会弹出个对话框显示新值。

?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
Person = Backbone.Model.extend({
        defaults: {
            name: 'Fetus',
            age: 0,
            children: []
        },
        initialize: function(){
            alert("欢迎来到这个无蛋的世界");
            this.bind("change:name"function(){
                var name = this.get("name"); // 'Stewie Griffin'
                alert("我的名字变为" + name );
            });
        },
        replaceNameAttr: function( name ){
            this.set({ name: name });
        }
    });
    var person = new Person({ name: "Thomas", age: 67, children: ['Ryan']});
    person.replaceNameAttr('Stewie Griffin'); // 改变后触手会alert()

-
可以单个属性监听,也可以直接监听所有的属性,如:'this.bind("change", function(){});'

2.6 提取,存储和销毁

Models实际上是collection的一部分,用于向服务器请求啥的。
本部分的教程关注的是个体models,咳咳,更多的功能请查看collection部分。

2.7 提示和技巧

2.7.1 获取当前所有的属性

?
1
2
3
4
5
6
var person = new Person({ name: "Thomas", age: 67, children: ['Ryan']});
    var attributes = person.toJSON(); // { name: "Thomas", age: 67, children: ['Ryan']}
    /* 返回对当前属性的copy */
    delete attributes;
    var attributes = person.attributes;
    /* 上行返回属性的直接引用,对其的任何改变就等于实例属性本身的改变,所以还是建议你使用.set()来进行属性的设置,因为还有backbone的监听呀 */

-

2.7.2 在设置或存储属性的时候进行数据校验

?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Person = Backbone.Model.extend({
        // 如果从validate中返回字符串了, Backbone就会抛个实例异常
        validate: function( attributes ){
            if( attributes.age < 0 && attributes.name != "Dr Manhatten" ){
                return "你的存在是个错误";
            }
        },
        initialize: function(){
            alert("欢迎来到这个报错的世界");
            this.bind("error"function(model, error){
                // 收到个错误,记录,警告,然后忘记它。╮(‵▽′)╭
                alert( error );
            });
        }
    });
    
    var person = new Person;
    person.set({ name: "Mary Poppins", age: -1 });
    // 会触发error,输出警告
    delete person;
    
    var person = new Person;
    person.set({ name: "Dr Manhatten", age: -1 });
// 上帝为何怜悯我们的灵魂,只因为打开的方式不对

-

3. 啥是view?

Backbone的views是用来体现应用的数据模型的。他们还可以用于监听事件。
本部分教程不会说关于views绑定models和collections的事情,重点说一下视图能力和如何使用views与JS模板库,特别是Underscore.js的 _.template。
我们使用jQuery 1.5操作DOM,当然也可以使用其他的库比如MooTools或Sizzle,但Backbone.js的官方文档说支持使用jQuery。
咳咳,也就是说Backbone.View事件在jQuery以外的库中可能无法使用。
好我们开始,这回我们要实现一个搜索框。

?
1
2
3
4
5
6
7
8
9
SearchView = Backbone.View.extend({
        initialize: function(){
            alert("Alerts suck.");
        }
    });
    
    // The initialize function is always called when instantiating a Backbone View.
    // Consider it the constructor of the class.
    var search_view = new SearchView;

-

3.1 “el”属性

“el”属性引用DOM对象。每个Backbone.js的view都会有个“el”属性。
如果没定义的话它会默认创建一个空的div元素。
来让我们设置个div#search_container到el属性上去,以保证这个Backbone.View是有效的。

?
01
02
03
04
05
06
07
08
09
10
11
<div id="search_container"></div>
    
<script type="text/javascript">
    SearchView = Backbone.View.extend({
        initialize: function(){
            alert("Alerts suck.");
        }
    });
    
    var search_view = new SearchView({ el: $("#search_container") });
</script>

-
注意:既然是绑定在了这个容器元素了,那就代表着所有的事件都是以此DOM元素为基础而触发的噢。

3.2 模板加载

Backbone.js是依赖于Underscore.js的,因为它包括micro-templating方案。
更多信息请参考Underscore.js'的官方文档去。
让我们实现一个“render()”方法进行视图初始化,“render()”方法会用jQuery加载我们的模板到“el”属性的容器中。

?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<div id="search_container"></div>
   
<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>
   
<script type="text/javascript">
    SearchView = Backbone.View.extend({
        initialize: function(){
            this.render();
        },
        render: function(){
            // Compile the template using underscore
            var template = _.template( $("#search_template").html(), {} );
            // Load the compiled HTML into the Backbone "el"
            this.el.html( template );
        }
    });
    
    var search_view = new SearchView({ el: $("#search_container") });
</script>

-
提示:可以将你的模板放到CDN中的单独文件里,这样用户使用就不用每次都下载了。

3.3 事件监听

在view加一个监听,可以用Backbone.View的“events”属性。
要记得,事件监听只能绑定到“el”属性的子元素中。
让我们绑定个“click”事件到按钮上去。

?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<div id="search_container"></div>
   
<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>
   
<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"
        },
        doSearch: function( event ){
            // Button clicked, you can access the element that was clicked with event.currentTarget
            alert( "Search for " + $("#search_input").val() );
        }
    });
    
    var search_view = new SearchView({ el: $("#search_container") });
</script>

-

3.4 提示和技巧

3.4.1 使用模板变量

?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<div id="search_container"></div>
   
<script type="text/template" id="search_template">
    <!-- Access template variables with <%= %> -->
    <label><%= search_label %></label>
    <input type="text" id="search_input" />
    <input type="button" id="search_button" value="Search" />
</script>
   
<script type="text/javascript">
     SearchView = Backbone.View.extend({
        initialize: function(){
            this.render();
        },
        render: function(){
            //Pass variables in using Underscore.js Template
            var variables = { search_label: "My Search" };
            // Compile the template using underscore
            var template = _.template( $("#search_template").html(), variables );
            // Load the compiled HTML into the Backbone "el"
            this.el.html( template );
        },
        events: {
            "click input[type=button]": "doSearch"
        },
        doSearch: function( event ){
            // Button clicked, you can access the element that was clicked with event.currentTarget
            alert( "Search for " + $("#search_input").val() );
        }
    });
    
    var search_view = new SearchView({ el: $("#search_container") });
</script>

-

4. 啥是collection?

Backbone的collections其实就是个有序的models集合。适用于以下情况;

  • Model: Student, Collection: ClassStudents

  • Model: Todo Item, Collection: Todo List

  • Model: Animals, Collection: Zoo

通常你的collection可能只用一个类型的model,但是models并不局限于某一类型的collection;

  • Model: Student, Collection: Gym Class

  • Model: Student, Collection: Art Class

  • Model: Student, Collection: English Class

关于Model/Collection,这里有一个通用的例子。

?
1
2
3
4
5
6
7
8
9
var Song = Backbone.Model.extend({
        initialize: function(){
            console.log("Music is the answer");
        }
    });
    
    var Album = Backbone.Collection.extend({
        model: Song
    });

-

4.1 构建 collection

现在我们来点实在的,下面的例子演示了collection的使用方法。

?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
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]

-

5. 啥是router?

?
1
2
3
4
5
//@注1:本章的所有实例都是在http://example.com顶级域名下实现的。
//如果是http://example.com/tset/test.html的形式,则路径标记#前不能加/。
//如:http://example.com/#/user/helphttp://example.com/test/test.html#/user/help
//@注2:Backbone.js 0.9.2 的取路径后的值是posts/12,匹配^/posts/([^/]+)$不能(/posts/:id转换后的正则表达式)。所以原参数要改为posts/:id,测试才能通过。
//以下有错误的地方标红不改,以此类推。

-
Backbone的routers其实就相当于你的应用URL的hash(#)。
如果你读过“啥是view?”,那你应该知道,他们并不符合传统的MVC语义,本章将会详细解释这一点。
尽管Backbone的“router”对任何程序和功能都很有用,但它需要URL的routing/history功能。
Routers的定义包括路径和映射函数,下面的例子就让我们定义一个路径。
还请注意,routes只解析url的“#”之后的标记。
你的应用的所有链接都应该是像“#/action”或“#action”这样。
(附加个斜杠看起来会更好一些,比如:http://example.com/#/user/help)

?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
<script>
    var AppRouter = Backbone.Router.extend({
        routes: {
            "*actions""defaultRoute" // matcheshttp://example.com/#anything-here
        },
        defaultRoute: function( actions ){
            // The variable passed in matches the variable in the route definition "actions"
            alert( actions );
        }
    });
    // Initiate the router
    var app_router = new AppRouter;
    // Start Backbone history a neccesary step for bookmarkable URL's
    Backbone.history.start();
    
</script>
    
_URL进行变化_
[Activate route](#action)
[Activate another route](#/route/action)

-
请注意:Backbone 0.5 (released 1. July 2011)之前的版本,Router被称作Controller。为了避免字义混淆,Backbone的开发人员把名字改成Router了。
因此,如果你发现自己使用了Backbone的一个旧版本,那你应该写Backbone.Controller.extend({ ** });

5.1 动态Routing

原始框架允许你的routes定义包含动静混合态路径参数。
例如,你可能想进行检索一个post的id,你的URL看起来可能会是“http://example.com/#/posts/12”。
一旦这个路径被激活,你就可以取到id的值并做相应的处理了。下面是这个例子的实现。

?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<script>
    var AppRouter = Backbone.Router.extend({
        routes: {
            "/posts/:id""getPost",//@注2
            "*actions""defaultRoute" // Backbone will try match the route above first
        },
        getPost: function( id ) {
            // Note the variable in the route definition being passed in here
            alert( "Get post number " + id );
        },
        defaultRoute: function( actions ){
            alert( actions );
        }
    });
    // Instantiate the router
    var app_router = new AppRouter;
    // Start Backbone history a neccesary step for bookmarkable URL's
    Backbone.history.start();
    
</script>
    
_URL进行改变_
[Post 120](#/posts/120)
[Post 130](#/posts/130)

-

5.2 动态路径对比——":params"和"*splats"

Backbone使用两种参数模式实现路径,第一种是":params"匹配URL路径内任何斜杠之间的字符串,而还有一种是"splats"匹配任意数量的URL路径。
注意"splat"的本质意思,他总是最后一个变量,因为它匹配的是URL路径之后的全部内容。
在route中定义的任何"*splats"或":params"都会以参数的形式穿入关联函数中。
比如一个route定义了"/:route/:action",那就会传入两个参数(“route”和“action”) 到回调函数中。
下面的示例使用":params"和"*splats"。

?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
routes: {
            "/posts/:id""getPost",//@注2,下同
            // <a href="http://example.com/#/posts/121">Example</a>
            "/download/*path""downloadFile",
            // <a href="http://example.com/#/download/user/images/hey.gif">Download</a>
            "/:route/:action""loadView",
            // <a href="http://example.com/#/dashboard/graph">LoadRoute/Action View</a>
        },
    
        getPost: function( id ){
            alert(id); // 121
        },
        downloadFile: function( path ){
            alert(path); // user/images/hey.gif
        },
        loadView: function( route, action ){
            alert(route + "_" + action); // dashboard_graph
        }

-

这是菜鸟与老油条,屌丝与高富帅的战争 —— 译者:茶几 @chajn

原作者:脱袜子·戴蕾丝  原文:http://backbonetutorials.com/

1. 为啥你需要Backbone.js?

我们知道,仅仅使用jQuery或MooTools啥的来构建web应用或复杂的用户界面是极其困难的。
主要是因为这些JS库都把心思花费在它们擅长做的事情上了,而并没有意识到的是,即便没有任何页面结构,也是可以构建完整的应用的。
用Backbone就可以很容易的把你的应用部署到一个通过jQuery回调的嵌套堆中,然后在页面生成DOM元素实体。

我不需要解释为什么没有结构的页面构建是一个坏主意。
你当然可以每次构造应用的时候都发明一种实现方式,但这只是瞬间的美丽,你错过的是后世的精彩!
那你将永远只能是个屌ser。Balabala……

2. 啥是model?

网络上对MVC的定义是比较尿的,你甚至不知道怎样去理解和怎样去做。
但backbone.js的作者对backbone.js的model表现的定义就很明确。
Models是任何JS应用的核心部分,包括数据交互和众多相关逻辑:格式转换,校验,属性计算,访问控制等。
好的,让我们先创建一个model:

?
1
2
3
4
5
6
7
Person = Backbone.Model.extend({
        initialize: function(){
            alert("Welcome to this world");
        }
    });
    
    var person = new Person;

-
SO我们看到new一个model的实例后就会触发initialize()函数(models,collections 和 views 的工作机制都是一样滴)。虽然可以不声明initialize,但我们可能会经常用到。

2.1 设置属性

现在我们想设置一些属性,有两种方式,可以在创建model实例时进行传参,也可以在实例生成后通过model.set(obj)来进行设置。  

?
01
02
03
04
05
06
07
08
09
10
11
Person = Backbone.Model.extend({
        initialize: function(){
            alert("Welcome to this world");
        }
    });
    
    var person = new Person({ name: "Thomas", age: 67});
    delete person;
    //或者用set,操作等价
    var person = new Person();
    person.set({ name: "Thomas", age: 67});

-
两种方式在功能上是一样的。有设置属性,就有读取属性,下一步我们来获取它们。

2.2 获取属性

很简单,用 model.get(name)方法就可以读取属性值了。

?
01
02
03
04
05
06
07
08
09
10
11
Person = Backbone.Model.extend({
        initialize: function(){
            alert("欢迎来到这个二蛋的世界");
        }
    });
    
    var person = new Person({ name: "Thomas", age: 67, children: ['Ryan']});
    
    var age = person.get("age"); // 67
    var name = person.get("name"); // "Thomas"
    var children = person.get("children"); // ['Ryan']

-

2.3 设置model默认属性

有的时候你可能会想让model有默认属性值。
没的问题,只要在进行model声明的时候设置个'defaults'就行了。

?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
Person = Backbone.Model.extend({
        defaults: {
            name: 'Fetus',
            age: 0,
            children: []
        },
        initialize: function(){
            alert("欢迎来到这个三蛋的世界");
        }
    });
    
    var person = new Person({ name: "Thomas", age: 67, children: ['Ryan']});
    
    var age = person.get("age"); // 67
    var name = person.get("name"); // "Thomas"
    var children = person.get("children"); // ['Ryan']

-

2.4 操纵model的属性

Models可以添加自定义方法来进行自身的属性修改,默认这些方法都是公开的。

?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
Person = Backbone.Model.extend({
        defaults: {
            name: 'Fetus',
            age: 0,
            children: []
        },
        initialize: function(){
            alert("欢迎来到这个四蛋的世界");
        },
        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: 67, children: ['Ryan']});
    person.adopt('John Resig');
    var children = person.get("children"); // ['Ryan', 'John Resig']

-
添加了自定义方法之后,我们就可以更好的控制实例的自身属性了。

2.5 监听model的属性改变

现在说点有用的,我们可以通过model.bind(event,callback)方法来绑定change事件来监听属性改变。
下面的这个例子就是在initialize方法中绑定了一个name属性改变的事件监听。
如果person的name属性改变了,就会弹出个对话框显示新值。

?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
Person = Backbone.Model.extend({
        defaults: {
            name: 'Fetus',
            age: 0,
            children: []
        },
        initialize: function(){
            alert("欢迎来到这个无蛋的世界");
            this.bind("change:name"function(){
                var name = this.get("name"); // 'Stewie Griffin'
                alert("我的名字变为" + name );
            });
        },
        replaceNameAttr: function( name ){
            this.set({ name: name });
        }
    });
    var person = new Person({ name: "Thomas", age: 67, children: ['Ryan']});
    person.replaceNameAttr('Stewie Griffin'); // 改变后触手会alert()

-
可以单个属性监听,也可以直接监听所有的属性,如:'this.bind("change", function(){});'

2.6 提取,存储和销毁

Models实际上是collection的一部分,用于向服务器请求啥的。
本部分的教程关注的是个体models,咳咳,更多的功能请查看collection部分。

2.7 提示和技巧

2.7.1 获取当前所有的属性

?
1
2
3
4
5
6
var person = new Person({ name: "Thomas", age: 67, children: ['Ryan']});
    var attributes = person.toJSON(); // { name: "Thomas", age: 67, children: ['Ryan']}
    /* 返回对当前属性的copy */
    delete attributes;
    var attributes = person.attributes;
    /* 上行返回属性的直接引用,对其的任何改变就等于实例属性本身的改变,所以还是建议你使用.set()来进行属性的设置,因为还有backbone的监听呀 */

-

2.7.2 在设置或存储属性的时候进行数据校验

?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Person = Backbone.Model.extend({
        // 如果从validate中返回字符串了, Backbone就会抛个实例异常
        validate: function( attributes ){
            if( attributes.age < 0 && attributes.name != "Dr Manhatten" ){
                return "你的存在是个错误";
            }
        },
        initialize: function(){
            alert("欢迎来到这个报错的世界");
            this.bind("error"function(model, error){
                // 收到个错误,记录,警告,然后忘记它。╮(‵▽′)╭
                alert( error );
            });
        }
    });
    
    var person = new Person;
    person.set({ name: "Mary Poppins", age: -1 });
    // 会触发error,输出警告
    delete person;
    
    var person = new Person;
    person.set({ name: "Dr Manhatten", age: -1 });
// 上帝为何怜悯我们的灵魂,只因为打开的方式不对

-

3. 啥是view?

Backbone的views是用来体现应用的数据模型的。他们还可以用于监听事件。
本部分教程不会说关于views绑定models和collections的事情,重点说一下视图能力和如何使用views与JS模板库,特别是Underscore.js的 _.template。
我们使用jQuery 1.5操作DOM,当然也可以使用其他的库比如MooTools或Sizzle,但Backbone.js的官方文档说支持使用jQuery。
咳咳,也就是说Backbone.View事件在jQuery以外的库中可能无法使用。
好我们开始,这回我们要实现一个搜索框。

?
1
2
3
4
5
6
7
8
9
SearchView = Backbone.View.extend({
        initialize: function(){
            alert("Alerts suck.");
        }
    });
    
    // The initialize function is always called when instantiating a Backbone View.
    // Consider it the constructor of the class.
    var search_view = new SearchView;

-

3.1 “el”属性

“el”属性引用DOM对象。每个Backbone.js的view都会有个“el”属性。
如果没定义的话它会默认创建一个空的div元素。
来让我们设置个div#search_container到el属性上去,以保证这个Backbone.View是有效的。

?
01
02
03
04
05
06
07
08
09
10
11
<div id="search_container"></div>
    
<script type="text/javascript">
    SearchView = Backbone.View.extend({
        initialize: function(){
            alert("Alerts suck.");
        }
    });
    
    var search_view = new SearchView({ el: $("#search_container") });
</script>

-
注意:既然是绑定在了这个容器元素了,那就代表着所有的事件都是以此DOM元素为基础而触发的噢。

3.2 模板加载

Backbone.js是依赖于Underscore.js的,因为它包括micro-templating方案。
更多信息请参考Underscore.js'的官方文档去。
让我们实现一个“render()”方法进行视图初始化,“render()”方法会用jQuery加载我们的模板到“el”属性的容器中。

?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<div id="search_container"></div>
   
<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>
   
<script type="text/javascript">
    SearchView = Backbone.View.extend({
        initialize: function(){
            this.render();
        },
        render: function(){
            // Compile the template using underscore
            var template = _.template( $("#search_template").html(), {} );
            // Load the compiled HTML into the Backbone "el"
            this.el.html( template );
        }
    });
    
    var search_view = new SearchView({ el: $("#search_container") });
</script>

-
提示:可以将你的模板放到CDN中的单独文件里,这样用户使用就不用每次都下载了。

3.3 事件监听

在view加一个监听,可以用Backbone.View的“events”属性。
要记得,事件监听只能绑定到“el”属性的子元素中。
让我们绑定个“click”事件到按钮上去。

?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<div id="search_container"></div>
   
<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>
   
<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"
        },
        doSearch: function( event ){
            // Button clicked, you can access the element that was clicked with event.currentTarget
            alert( "Search for " + $("#search_input").val() );
        }
    });
    
    var search_view = new SearchView({ el: $("#search_container") });
</script>

-

3.4 提示和技巧

3.4.1 使用模板变量

?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<div id="search_container"></div>
   
<script type="text/template" id="search_template">
    <!-- Access template variables with <%= %> -->
    <label><%= search_label %></label>
    <input type="text" id="search_input" />
    <input type="button" id="search_button" value="Search" />
</script>
   
<script type="text/javascript">
     SearchView = Backbone.View.extend({
        initialize: function(){
            this.render();
        },
        render: function(){
            //Pass variables in using Underscore.js Template
            var variables = { search_label: "My Search" };
            // Compile the template using underscore
            var template = _.template( $("#search_template").html(), variables );
            // Load the compiled HTML into the Backbone "el"
            this.el.html( template );
        },
        events: {
            "click input[type=button]": "doSearch"
        },
        doSearch: function( event ){
            // Button clicked, you can access the element that was clicked with event.currentTarget
            alert( "Search for " + $("#search_input").val() );
        }
    });
    
    var search_view = new SearchView({ el: $("#search_container") });
</script>

-

4. 啥是collection?

Backbone的collections其实就是个有序的models集合。适用于以下情况;

  • Model: Student, Collection: ClassStudents

  • Model: Todo Item, Collection: Todo List

  • Model: Animals, Collection: Zoo

通常你的collection可能只用一个类型的model,但是models并不局限于某一类型的collection;

  • Model: Student, Collection: Gym Class

  • Model: Student, Collection: Art Class

  • Model: Student, Collection: English Class

关于Model/Collection,这里有一个通用的例子。

?
1
2
3
4
5
6
7
8
9
var Song = Backbone.Model.extend({
        initialize: function(){
            console.log("Music is the answer");
        }
    });
    
    var Album = Backbone.Collection.extend({
        model: Song
    });

-

4.1 构建 collection

现在我们来点实在的,下面的例子演示了collection的使用方法。

?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
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]

-

5. 啥是router?

?
1
2
3
4
5
//@注1:本章的所有实例都是在http://example.com顶级域名下实现的。
//如果是http://example.com/tset/test.html的形式,则路径标记#前不能加/。
//如:http://example.com/#/user/helphttp://example.com/test/test.html#/user/help
//@注2:Backbone.js 0.9.2 的取路径后的值是posts/12,匹配^/posts/([^/]+)$不能(/posts/:id转换后的正则表达式)。所以原参数要改为posts/:id,测试才能通过。
//以下有错误的地方标红不改,以此类推。

-
Backbone的routers其实就相当于你的应用URL的hash(#)。
如果你读过“啥是view?”,那你应该知道,他们并不符合传统的MVC语义,本章将会详细解释这一点。
尽管Backbone的“router”对任何程序和功能都很有用,但它需要URL的routing/history功能。
Routers的定义包括路径和映射函数,下面的例子就让我们定义一个路径。
还请注意,routes只解析url的“#”之后的标记。
你的应用的所有链接都应该是像“#/action”或“#action”这样。
(附加个斜杠看起来会更好一些,比如:http://example.com/#/user/help)

?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
<script>
    var AppRouter = Backbone.Router.extend({
        routes: {
            "*actions""defaultRoute" // matcheshttp://example.com/#anything-here
        },
        defaultRoute: function( actions ){
            // The variable passed in matches the variable in the route definition "actions"
            alert( actions );
        }
    });
    // Initiate the router
    var app_router = new AppRouter;
    // Start Backbone history a neccesary step for bookmarkable URL's
    Backbone.history.start();
    
</script>
    
_URL进行变化_
[Activate route](#action)
[Activate another route](#/route/action)

-
请注意:Backbone 0.5 (released 1. July 2011)之前的版本,Router被称作Controller。为了避免字义混淆,Backbone的开发人员把名字改成Router了。
因此,如果你发现自己使用了Backbone的一个旧版本,那你应该写Backbone.Controller.extend({ ** });

5.1 动态Routing

原始框架允许你的routes定义包含动静混合态路径参数。
例如,你可能想进行检索一个post的id,你的URL看起来可能会是“http://example.com/#/posts/12”。
一旦这个路径被激活,你就可以取到id的值并做相应的处理了。下面是这个例子的实现。

?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<script>
    var AppRouter = Backbone.Router.extend({
        routes: {
            "/posts/:id""getPost",//@注2
            "*actions""defaultRoute" // Backbone will try match the route above first
        },
        getPost: function( id ) {
            // Note the variable in the route definition being passed in here
            alert( "Get post number " + id );
        },
        defaultRoute: function( actions ){
            alert( actions );
        }
    });
    // Instantiate the router
    var app_router = new AppRouter;
    // Start Backbone history a neccesary step for bookmarkable URL's
    Backbone.history.start();
    
</script>
    
_URL进行改变_
[Post 120](#/posts/120)
[Post 130](#/posts/130)

-

5.2 动态路径对比——":params"和"*splats"

Backbone使用两种参数模式实现路径,第一种是":params"匹配URL路径内任何斜杠之间的字符串,而还有一种是"splats"匹配任意数量的URL路径。
注意"splat"的本质意思,他总是最后一个变量,因为它匹配的是URL路径之后的全部内容。
在route中定义的任何"*splats"或":params"都会以参数的形式穿入关联函数中。
比如一个route定义了"/:route/:action",那就会传入两个参数(“route”和“action”) 到回调函数中。
下面的示例使用":params"和"*splats"。

?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
routes: {
            "/posts/:id""getPost",//@注2,下同
            // <a href="http://example.com/#/posts/121">Example</a>
            "/download/*path""downloadFile",
            // <a href="http://example.com/#/download/user/images/hey.gif">Download</a>
            "/:route/:action""loadView",
            // <a href="http://example.com/#/dashboard/graph">LoadRoute/Action View</a>
        },
    
        getPost: function( id ){
            alert(id); // 121
        },
        downloadFile: function( path ){
            alert(path); // user/images/hey.gif
        },
        loadView: function( route, action ){
            alert(route + "_" + action); // dashboard_graph
        }

-

原创粉丝点击