Skip to content

Instantly share code, notes, and snippets.

@xiaoDC
Forked from nightire/How to use Backbone.js.md
Last active August 29, 2015 14:21
Show Gist options
  • Select an option

  • Save xiaoDC/30f8962f13ce1cf9242e to your computer and use it in GitHub Desktop.

Select an option

Save xiaoDC/30f8962f13ce1cf9242e to your computer and use it in GitHub Desktop.

Revisions

  1. @nightire nightire revised this gist Mar 27, 2013. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion How to use Backbone.js.md
    Original file line number Diff line number Diff line change
    @@ -279,7 +279,7 @@ _注:现在的 jQuery 早已经全面采用 `on` 方法来执行事件委托
    var PersonView = Backbone.View.extend({
    // ...
    input: _.template('<input type="text"><%= name %></input>'),
    submit: _.template('<button type="submit">Change Name</button>),
    submit: _.template('<button type="submit">Change Name</button>'),

    render: function() {
    data = this.model.toJSON();
  2. @nightire nightire revised this gist Mar 23, 2013. 1 changed file with 33 additions and 29 deletions.
    62 changes: 33 additions & 29 deletions How to use Backbone.js.md
    Original file line number Diff line number Diff line change
    @@ -5,20 +5,20 @@
    ### 创建 Model

    ```javascript
    var PersonModel = Backbone.Model.extend({});
    var Person = Backbone.Model.extend();
    ```

    ### 实例化 && 读写

    ```javascript
    var personModel = new AppModel({
    var person = new Person({
    id: 1,
    name: "Albert Yu"
    });

    personModel.get("name"); // "Albert Yu"
    person.get("name"); // "Albert Yu"

    personModel.set({name: "Yu Fan"});
    person.set({name: "Yu Fan"});
    ```

    ### 创建 View
    @@ -44,7 +44,7 @@ var PersonView = Backbone.View.extend({

    ```javascript
    var personView = new PersonView({
    model: personModel;
    model: person;
    })

    personView.render();
    @@ -58,18 +58,18 @@ console.log(personView.el); // check the el
    在创建 Model 的时候,可以指定缺省数据:

    ```javascript
    var PersonModel = Backbone.Model.extend({
    var Person = Backbone.Model.extend({
    defaults: {
    name: "unnamed";
    dob: new Date()
    }
    });
    ```

    **注意**:在 Javascript 中,对象是通过引用传递的,这就意味着动态生成的数据会保持第一次执行时的状态。像上面那个例子,每次初始化一个 `PersonModel` 的实例都会产生相同的 `dob` 数字。可以把 `defaults` 定义为一个函数来解决这个问题。如下:
    **注意**:在 Javascript 中,对象是通过引用传递的,这就意味着动态生成的数据会保持第一次执行时的状态。像上面那个例子,每次初始化一个 `Person` 的实例都会产生相同的 `dob` 数字。可以把 `defaults` 定义为一个函数来解决这个问题。如下:

    ```javascript
    var PersonModel = Backbone.Model.extend({
    var Person = Backbone.Model.extend({
    defaults: function() {
    return {
    name: "unnamed";
    @@ -82,30 +82,30 @@ var PersonModel = Backbone.Model.extend({
    ### 从服务器获得数据(RESTful way)

    ```javascript
    var PersonModel = Backbone.Model.extend({urlRoot: "/people"}); // 指定数据来源
    var Person = Backbone.Model.extend({urlRoot: "/people"}); // 指定数据来源

    var person = new PersonModel({id: 1}); // 指定对象目标
    var person = new Person({id: 1}); // 指定对象目标

    person.fetch(); // GET /people/1

    person.set({name: "Albert Yu"}); // 修改数据属性
    person.save(); // PUT /people/1

    var anotherPerson = new PersonModel(); // 生成新对象
    anotherPerson.set({name: "John Doe"}); // 设置对象属性
    anotherPerson.save(); // POST /people
    var newPerson = new PersonModel(); // 生成新对象
    newPerson.set({name: "John Doe"}); // 设置对象属性
    newPerson.save(); // POST /people

    anotherPerson.get("id"); // 2
    anotherPerson.destroy(); // DELETE /people/2
    newPerson.get("id"); // 2
    newPerson.destroy(); // DELETE /people/2
    ```

    ### 监听对象的变化(并做出响应)

    ```javascript
    var person = new PersonModel({id: 1}); // 生成实例对象
    var person = new Person({id: 1}); // 生成实例对象

    person.on("change", function() {
    alert("Something changed on you!");
    alert("Something changed on " + this.get("name") + " you!");
    });
    ```

    @@ -114,7 +114,7 @@ person.on("change", function() {
    - 另外,我们还可以只针对对象的个别属性进行监听:

    ```javascript
    var person = new PersonModel({id: 1});
    var person = new Person({id: 1});

    person.on("change:name", function() { // 现在只监听 name 属性是否变化
    alert("Your name has changed!");
    @@ -124,7 +124,7 @@ person.on("change:name", function() { // 现在只监听 name 属性是否变化
    ### 将数据转化成 JSON 对象格式

    ```javascript
    var person = new PersonModel({id: 1});
    var person = new Person({id: 1});

    console.log(person.toJSON());

    @@ -142,11 +142,11 @@ var PersonView = Backbone.View.extend({
    id: "personOne",
    class: "person",
    attributes: {
    title: this.model.get("user")
    title: this.model.get("name")
    },

    render: function() {
    var person = "<p>" + this.model.get("user") + "</p>";
    var person = "<p>" + this.model.get("name") + "</p>";
    $(this.el).html(person);
    }
    });
    @@ -192,7 +192,7 @@ var PersonView = Backbone.View.extend({
    id: "personOne",
    class: "person",
    attributes: {
    title: this.model.get("user")
    title: this.model.get("name")
    },

    template: _.template('<p><%= name %></p>'),
    @@ -225,7 +225,7 @@ var PersonView = Backbone.View.extend({
    id: "personOne",
    class: "person",
    attributes: {
    title: this.model.get("user")
    title: this.model.get("name")
    },

    template: _.template('<p><%= name %></p>'),
    @@ -265,7 +265,9 @@ _注:现在的 jQuery 早已经全面采用 `on` 方法来执行事件委托

    是时候了解些复杂的东东了!首先我们根据目前所学,知道了 Backbone.js 的(部分)内部构造:

    >
    > 服务器 <=(获取)模型(数据)=> 视图(渲染)=> DOM
    >
    这个过程当然也可以反向回来形成一个回圈。

    @@ -304,7 +306,7 @@ var PersonView = Backbone.View.extend({
    return;
    }
    }
    }
    });
    ```
    Well… 这么做的确能行,但问题是应该属于 Model 处理的逻辑现在散落在 View 当中,这样不太妥呀!没关系,我们重构一下,这一次 View 的工作仅仅是通知:
    @@ -322,7 +324,7 @@ var PersonView = Backbone.View.extend({
    }
    });
    var PersonModel = Backbone.Model.extend({
    var Person = Backbone.Model.extend({
    // ...
    updateName: function(newName) {
    if (newName !== this.get('name')) {
    @@ -339,7 +341,9 @@ var PersonModel = Backbone.Model.extend({
    现在,回路模型就像这样:
    >
    > 服务器 <=(更新)模型(处理)<=(通知)视图 <=(用户交互)DOM
    >
    不过事情还没有结束:如果模型的数据变化了,视图又如何知道呢?你或许立刻想到可以在视图完成通知之后立刻执行 `render` 方法:
    @@ -378,14 +382,14 @@ var PersonView = Backbone.View.extend({
    ```javascript
    var People = Backbone.Collection.extend({
    model: Person
    model: person // 之前已经生成的模型实例
    });
    ```
    看起来挺像 Model 的,不过这一次获得的数据都是数组了。我们需要用处理数组的办法来处理集合里的数据(当然,Backbone.js 提供了许多内置方法):
    ```javascript
    var people = new.People({});
    var people = new.People();
    people.add([
    { name: "John Doe", id: 1 },
    @@ -407,7 +411,7 @@ var People = Backbone.Collection.extend({
    url: '/people'
    });
    var people = new.People({});
    var people = new.People();
    people.fetch(); // 可以批量获取了
    people.add(person1);
    @@ -423,7 +427,7 @@ people.reset(); // reset 方法可以重置发生了变化的集合实例,确
    ```javascript
    people.on("reset", function() {
    alert("You have " + people.length + " people now!");
    alert("You have " + this.length + " people now!");
    });
    people.fetch(); // will alert
  3. @nightire nightire revised this gist Mar 23, 2013. 1 changed file with 4 additions and 4 deletions.
    8 changes: 4 additions & 4 deletions How to use Backbone.js.md
    Original file line number Diff line number Diff line change
    @@ -12,7 +12,7 @@ var PersonModel = Backbone.Model.extend({});

    ```javascript
    var personModel = new AppModel({
    id: 1,
    id: 1,
    name: "Albert Yu"
    });

    @@ -570,7 +570,7 @@ var App = Backbone.Router.extend({
    var people = new People();
    people.fetch();
    var peopleView = new PeopleView({collection: people});
    $(#app).append(peopleView.render().el);
    $("#app").append(peopleView.render().el);
    },
    ...
    @@ -584,7 +584,7 @@ var App = Backbone.Router.extend({
    initialize: function() {
    this.people = new People();
    this.peopleView = new PeopleView({collection: this.people});
    $(#app).append(this.peopleView.el);
    $("#app").append(this.peopleView.el);
    },
    index: function() {
    @@ -604,7 +604,7 @@ var App = new (Backbone.Router.extend({
    initialize: function() {
    this.people = new People();
    this.peopleView = new PeopleView({collection: this.people});
    $(#app).append(this.peopleView.el);
    $("#app").append(this.peopleView.el);
    },
    index: function() {
  4. @nightire nightire created this gist Mar 23, 2013.
    626 changes: 626 additions & 0 deletions How to use Backbone.js.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,626 @@
    # [Backbone.js](http://backbonejs.org/)

    ## Simple Starting Point

    ### 创建 Model

    ```javascript
    var PersonModel = Backbone.Model.extend({});
    ```

    ### 实例化 && 读写

    ```javascript
    var personModel = new AppModel({
    id: 1,
    name: "Albert Yu"
    });

    personModel.get("name"); // "Albert Yu"

    personModel.set({name: "Yu Fan"});
    ```

    ### 创建 View

    ```javascript
    var PersonView = Backbone.View.extend({
    render: function() {
    var person = "<p>" + this.model.get("user") + "</p>";
    $(this.el).html(person);
    }
    });
    ```

    最后一行的 `this.el` 蕴含了丰富的意义:

    1. 每一个 View Model 事实上创建了一个页面作用域(Page Scope),`el` 即是该作用域下的顶级标签,其默认标签是 `<div></div>`

    2. `this` 在这里就是指代了此 View Model 所定义的作用域;

    3. `render` 方法里生成的页面元素都会被包裹在 `el` 之内,并受到 `this` 的控制。

    ### 实例化 && 添加页面元素(获取 Model 的数据)

    ```javascript
    var personView = new PersonView({
    model: personModel;
    })

    personView.render();
    console.log(personView.el); // check the el
    ```

    ## Better Model

    ### 缺省数据

    在创建 Model 的时候,可以指定缺省数据:

    ```javascript
    var PersonModel = Backbone.Model.extend({
    defaults: {
    name: "unnamed";
    dob: new Date()
    }
    });
    ```

    **注意**:在 Javascript 中,对象是通过引用传递的,这就意味着动态生成的数据会保持第一次执行时的状态。像上面那个例子,每次初始化一个 `PersonModel` 的实例都会产生相同的 `dob` 数字。可以把 `defaults` 定义为一个函数来解决这个问题。如下:

    ```javascript
    var PersonModel = Backbone.Model.extend({
    defaults: function() {
    return {
    name: "unnamed";
    dob: new Date()
    }
    }
    });
    ```

    ### 从服务器获得数据(RESTful way)

    ```javascript
    var PersonModel = Backbone.Model.extend({urlRoot: "/people"}); // 指定数据来源

    var person = new PersonModel({id: 1}); // 指定对象目标

    person.fetch(); // GET /people/1

    person.set({name: "Albert Yu"}); // 修改数据属性
    person.save(); // PUT /people/1

    var anotherPerson = new PersonModel(); // 生成新对象
    anotherPerson.set({name: "John Doe"}); // 设置对象属性
    anotherPerson.save(); // POST /people

    anotherPerson.get("id"); // 2
    anotherPerson.destroy(); // DELETE /people/2
    ```

    ### 监听对象的变化(并做出响应)

    ```javascript
    var person = new PersonModel({id: 1}); // 生成实例对象

    person.on("change", function() {
    alert("Something changed on you!");
    });
    ```

    注册在 `person` 对象下的 `change` 事件是 [Backbone.js 的内置事件](http://documentcloud.github.com/backbone/#Events-catalog)之一。

    - 另外,我们还可以只针对对象的个别属性进行监听:

    ```javascript
    var person = new PersonModel({id: 1});

    person.on("change:name", function() { // 现在只监听 name 属性是否变化
    alert("Your name has changed!");
    });
    ```

    ### 将数据转化成 JSON 对象格式

    ```javascript
    var person = new PersonModel({id: 1});

    console.log(person.toJSON());

    // or, maybe you need this:
    console.log(JSON.stringify(person));
    ```

    ## Better View

    ### 定义 View 的顶级标签(el)属性

    ```javascript
    var PersonView = Backbone.View.extend({
    tagName: "article",
    id: "personOne",
    class: "person",
    attributes: {
    title: this.model.get("user")
    },

    render: function() {
    var person = "<p>" + this.model.get("user") + "</p>";
    $(this.el).html(person);
    }
    });
    ```

    这将会生成下面这样的 HTML 片段:

    ```html
    <article id="personOne" class="person" title="Albert Yu">
    <p> Albert Yu </p>
    </article>
    ```

    ### 缓存化的 jQuery 对象

    像上面的 HTML 代码,如果要使用 jQuery 操作它们,那就像这样:

    ```javascript
    $("#personOne").html();
    ```

    当然,Backbone.js 提供了更好的选择:

    ```javascript
    $(this.el).html();
    ```

    甚至更好:

    ```javascript
    this.$el.html();
    ```

    `$el` 是 Backbone.js 为 `el` 生成的 jQuery 对象的缓存,使其可以反复使用而不用担心产生多个 jQuery 的实例对象。

    ### 内置的模板引擎(underscore template)

    之前的 `render` 方法可不怎么优雅,好在 Backbone.js 内置了 [Underscore.js](http://underscorejs.org/) 库,其中包含了一个简洁的模板引擎:

    ```javascript
    var PersonView = Backbone.View.extend({
    tagName: "article",
    id: "personOne",
    class: "person",
    attributes: {
    title: this.model.get("user")
    },

    template: _.template('<p><%= name %></p>'),

    render: function() {
    data = this.model.toJSON();
    $(this.el).html(template(data));
    }
    });
    ```

    Cool! 这样就好看多啦~

    如果内置模板引擎不合你的胃口,你当然可以选用其他的方案,比如说:

    - [ECO](https://github.com/sstephenson/eco)
    - [Mustache.js](http://mustache.github.com/)
    - [Handlebars.js](http://handlebarsjs.com/)
    - ...

    ### 视图事件

    Backbone.js 能够监视数据对象的变化并为其注册事件及回调函数,当然对于视图元素也是一样的,并且由于视图对象为我们提供了页面作用域的控制,使得我们可以更简单地和作用域内的对象交互而不至于发生混乱。

    继续扩展上面的例子:

    ```javascript
    var PersonView = Backbone.View.extend({
    tagName: "article",
    id: "personOne",
    class: "person",
    attributes: {
    title: this.model.get("user")
    },

    template: _.template('<p><%= name %></p>'),

    render: function() {
    data = this.model.toJSON();
    $(this.el).html(template(data));
    },

    events: {
    "mouseover" : "showMore",
    "click p" : "greeting"
    },

    showMore: function() {...},
    greeting: function() {...}
    });
    ```

    视图事件的语法结构是:`"<event> <selector>: <method>"`,解读一下上面注册的两个事件:

    1. `mouseover` 事件被注册在作用域内的顶级标签上(即 `el`,也就是本例中的 `<article></article>`),于是 `showMore` 方法会在鼠标经过 `this.el` 标签的时候触发执行;

    2. 类似的,`click` 事件被注册在 `<p></p>` 标签上……等一下,哪一个 `p` 标签?

    OK,这就是作用域的体现了,事实上 Backbone.js 执行此事件调用时的后台代码差不多是这样的:

    ```javascript
    $(this.el).delegate('p', 'click', greeting);
    ```

    _注:现在的 jQuery 早已经全面采用 `on` 方法来执行事件委托调用了,这个例子只是为了强调这是一种委托调用而已。_

    我们可以看到,事件的委托方法(delegate)执行在 `this.$el` 这个 jQuery 对象上,因此 `p` 就是被包裹在 `this.el` 之内的那一个;事实上你可以用完全兼容的 CSS 选择符来指定你要的元素,这和使用 jQuery 是一样的!只不过 Backbone.js 事先帮你选择了一个上级作用域,使得筛选工作变得更简单,更具针对性。

    ## 进阶:模型与视图的交互

    是时候了解些复杂的东东了!首先我们根据目前所学,知道了 Backbone.js 的(部分)内部构造:

    > 服务器 <=(获取)模型(数据)=> 视图(渲染)=> DOM
    这个过程当然也可以反向回来形成一个回圈。

    ### 视图的变化通知模型

    我们先让视图能够接受用户交互并产生变化

    ```javascript
    var PersonView = Backbone.View.extend({
    // ...
    input: _.template('<input type="text"><%= name %></input>'),
    submit: _.template('<button type="submit">Change Name</button>),
    render: function() {
    data = this.model.toJSON();
    $(this.el).html(input(data)).html(submit);
    }
    });
    ```
    现在咱们可以改名了,问题是模型如何知道数据发生了改变呢?这就需要视图通过事件通知它了:
    ```javascript
    var PersonView = Backbone.View.extend({
    // ...
    events: {
    "submit button": "updateName"
    }
    updateName: function() {
    newName = $(this).val(); // this = button!
    if (newName !== this.model.get('name')) {
    this.model.set({name: newName});
    } else {
    return;
    }
    }
    }
    ```
    Well… 这么做的确能行,但问题是应该属于 Model 处理的逻辑现在散落在 View 当中,这样不太妥呀!没关系,我们重构一下,这一次 View 的工作仅仅是通知:
    ```javascript
    var PersonView = Backbone.View.extend({
    // ...
    events: {
    "submit button": "updateName"
    }
    updateName: function() {
    newName = $(this).val();
    this.model.updateName(newName);
    }
    });
    var PersonModel = Backbone.Model.extend({
    // ...
    updateName: function(newName) {
    if (newName !== this.get('name')) {
    this.set({name: newName});
    this.save(); // 通知服务器保存
    } else {
    return;
    }
    }
    });
    ```
    就像这样,我们通过参数传递把改变的值转交给 Model,再由 Model 做进一步的处理就是了。
    现在,回路模型就像这样:
    > 服务器 <=(更新)模型(处理)<=(通知)视图 <=(用户交互)DOM
    不过事情还没有结束:如果模型的数据变化了,视图又如何知道呢?你或许立刻想到可以在视图完成通知之后立刻执行 `render` 方法:
    ```javascript
    var PersonView = Backbone.View.extend({
    // ...
    updateName: function() {
    newName = $(this).val();
    this.model.updateName(newName);
    this.render();
    }
    });
    ```
    嗯,好主意……不过它并不总是有用的,因为模型的值很有可能会在别处发生改变,比如在另外一个视图里。那么,要如何通知指定的视图响应模型数据的变化呢?
    这件事情需要在视图实例化的时候就去做,让视图去监听模型的变化吧:
    ```javascript
    var PersonView = Backbone.View.extend({
    // ... 省略其他的代码,添加以下初始化代码
    initialize: function() {
    this.listenTo(this.model, "change", this.render);
    // this.model.on("change", this.render, this);
    }
    });
    ```
    被注释掉的那一行是以前的另外一种写法,也能达到目的,但是稍微难以理解一点——最后一个参数传递的是视图实例对象本身。
    ## 数据集合
    ### 收集多个模型数据
    当模型的实例越来越多的时候,为了方便的处理它们,我们通常会想到用数组把它们集中起来。Backbone.js 提供了一个 Collection 模块来帮助我们简化这些事情:
    ```javascript
    var People = Backbone.Collection.extend({
    model: Person
    });
    ```
    看起来挺像 Model 的,不过这一次获得的数据都是数组了。我们需要用处理数组的办法来处理集合里的数据(当然,Backbone.js 提供了许多内置方法):
    ```javascript
    var people = new.People({});
    people.add([
    { name: "John Doe", id: 1 },
    { name: "Jane Smith", id: 2 }
    ]);
    people.length // => 2
    people.get(2) // => { name: "Jane Smith", id: 2 }
    people.at(0) // => { name: "John Doe", id: 1 }
    ```
    如果数据来自于服务器,那就需要指明 `url`:
    ```javascript
    var People = Backbone.Collection.extend({
    model: Person,
    url: '/people'
    });
    var people = new.People({});
    people.fetch(); // 可以批量获取了
    people.add(person1);
    people.reset(); // reset 方法可以重置发生了变化的集合实例,确保里面的数据是完整的
    ```
    ### 集合事件
    集合事件的注册及使用和模型非常相似,只不过集合多了几个专属的事件,这些差别可以通过查阅文档来获知。
    我们可以在触发事件的时候传递 `silent` 参数进去,阻止事件回调函数的执行:
    ```javascript
    people.on("reset", function() {
    alert("You have " + people.length + " people now!");
    });
    people.fetch(); // will alert
    people.fetch({silent: true}); // will not alert
    ```
    ### 集合视图
    集合视图的故事也没什么特别之处,只不过这一次视图里要处理的是一组数据,所以免不了要渲染两个层级,一层是集合,一层是遍历集合里的模型:
    ```javascript
    var PeopleView = Backbone.View.extend({
    render: function() { // 集合视图的 render 当然要渲染整个集合了
    this.collection.forEach(this.addPerson, this)
    },
    addPerson: function(person) { // 这还是老的模型视图那一套
    var personView = new PersonView({model: person});
    this.$el.append(personView.render().el);
    }
    });
    var peopleView = PeopleView.new({
    collection: people; // people 是之前定义过的集合实例
    })
    ```
    ### 监听集合的变化
    Same old stories...
    ```javascript
    var PeopleView = Backbone.View.extend({
    initialize: {
    this.collection.on("change", this.render, this), // 集合发生了变化,比如 reset, fetch
    this.collection.on("add", this.addPerson, this), // 集合里添加了新的数据
    this.model.on("hide", this.removePerson, this) // 集合里某一个数据被移除了
    },
    render: function() {
    this.collection.forEach(this.addPerson, this)
    },
    addPerson: function(person) {…},
    removePerson: function() {
    this.$el.remove();
    }
    });
    ```
    ...but not always as old as
    前两个事件处理都不难理解,关键是第三个:
    1. 集合中的某个数据被移除了(注意,不是这个数据在服务器那里删除了,而是把它从集合中移出去了),这件事情发生在 `collection.remove(item)` 的时候,按道理我们应该监听 collection 的;
    2. 然而在视图里真正发生改变的仅仅是那个数据,而不是全部集合,换句话说,视图里执行回调函数接受的 `this` 是那个数据,而不是整个集合;
    3. 这就是为什么用 `this.model.on` 而不是 `this.collection.on` 的原因。但是——数据是被 `collection.remove(item)` 移出,而不是它自身的方法调用,我们监听 `this.model.on` 要怎么知道它确切发生了呢?
    OK,这是一件稍微复杂一点的事情,在 collection 内部事实上发生了以下的变化:
    ```javascript
    var People = Backbone.Collection.extend({
    initialize: {
    this.on("remove", this.hideModel)
    },
    hideModel: function(model) {
    model.trigger("hide"); // 我们让 model 触发自定义事件 hide
    }
    });
    ```
    就这样,在视图里我们不监听 collection 的 remove 事件(因为我们不打算改变 collection),当 `collection.remove(item)` 发生的时候,collection 内部会让那个被移除的 item 触发它自己的自定义事件 `hide`。于是,我们就可以在视图里监听这个自定义事件,一旦监听到就说明 `collection.remove(item)` 确实发生了,然后就把 item 给 remove 掉。
    ## 让 App 飞~
    So far so good, 但是我们始终在一页里面动作,这对用户来讲是远远不够的。如果 url 发生了改变会怎样?
    在浏览器的世界里,使用 javascript 来控制 url 的历史记录有两种主要方式:
    ### Hash mark(#people/1)
    当有这样的链接存在时:
    ```html
    <a href="#people/1">Albert Yu</a>
    ```
    点击后,浏览器的 url 会是这个样子:
    ```
    http://www.example.com/#people/1
    ```
    于是,我们可以获得用户的 url 历史记录并控制它们。但是,这样并不够好,我们真正想要的是这样的 url:
    ```
    http://www.example.com/people/1
    ```
    只不过若是没有任何处理的话,访问这样的 url 会造成浏览器刷新,所有数据重新载入,这就体现不出我们使用现代 javascript 技术的优势了。
    Backbone.js 可以处理 Hash mark 的 url 历史记录,但我们重点要讲的是另外一种方式,来自新的 HTML5 API
    ### HTML5 Push State API
    使用 Push State,我们可以截取常规的 url 链接,然后专用 javascript 去处理它们,而不是让整个浏览器刷新。要启用对 Push State 的支持,我们需要调用 Backbone.js 的 history api:
    ```javascript
    Backbone.history.start({pushState: true});
    ```
    要截取 url 的变化转交由 Backbone.js 来处理,我们就要注册 routes:
    ```javascript
    var App = Backbone.Router.extend({
    routes: {
    "people" : "index", // /people
    "people/:id" : "show", // people/1
    "help/:subject/p:page" : "help" // help/intro/p3
    },
    index: function() {
    ...
    },
    show: function(id) {
    ...
    },
    help: function(subject, page) {
    ...
    }
    });
    ```
    那些忽略了内部实现的方法,事实上就是之前我们所学到的一切,这要根据你的应用程序来定。比如说 index 方法,我们就是想要获取 people collection ,然后把它们都显示出来,于是:
    ```javascript
    var App = Backbone.Router.extend({
    ...
    index: function() {
    var people = new People();
    people.fetch();
    var peopleView = new PeopleView({collection: people});
    $(#app).append(peopleView.render().el);
    },
    ...
    });
    ```
    或者,我们可以直接初始化集合与视图的实例:
    ```javascript
    var App = Backbone.Router.extend({
    initialize: function() {
    this.people = new People();
    this.peopleView = new PeopleView({collection: this.people});
    $(#app).append(this.peopleView.el);
    },
    index: function() {
    this.people.fetch();
    },
    ...
    });
    ```
    ## Wrap up!
    重新组织一下我们应用的入口吧,与其创建一堆类然后再分别实例化,我们完全可以一起做了:
    ```javascript
    var App = new (Backbone.Router.extend({
    initialize: function() {
    this.people = new People();
    this.peopleView = new PeopleView({collection: this.people});
    $(#app).append(this.peopleView.el);
    },
    index: function() {
    this.people.fetch();
    },
    ...
    start: function() { // 封装一下 Backbone.history API
    Backbone.history.start({pushState: true});
    }
    }));
    // 于是,我们可以这样初始化整个应用程序
    $(function() { App.start(); });
    ```
    最后,一休哥说了:“就到这里,再见吧!”