用Backbone.js来构建可扩展的应用程序 (Boilerplate)

Backbone.js是一个用JS写的MVC,它非常小(压缩过的源码只有5KB),它可以用来构建单页的web application.不像其他的相类似的库,它的使用是非常灵活的,除了一些基本的内容,其他的设计完全依赖于你自己的设计。

下面将会用Backboe Boilerplate来构建一个简单的图书馆应用程序,它非常简单,不过,你可以用它来扩充成更健壮的其他应用程序。

Backbone是什么?

Backbone.js是一个js为,用它可以轻松的来构建page web application.它非常的灵活,并且非常的轻量级,当然它的文档,社区也是比较成熟的,这就是为什么它这么火的原因。

Require.js是一个module loader(它遵循的是AMD design pattern),它允许你异步的加载js module和它们依赖的文件。

Underscore.js是一个库,它提供了一组非常有用的工具函数,它提供了可以迭代访问collection的能力,测试code是不是函数,还有一个内置的模板语言

什么是Backbone Boilerplate?

它包括了一系列的最优的如何使用backbone以及一组工具.它不是一个额外的库,但是它包括了一组库,并且鼓励我们用它所提供的结构来创建我们的backboe项目。

 

如何来安装Boilerplate?

 the Github repository下载code,

 

第一个First Module, The Book

 

当你用Backbone Boilerplate(或者是用AMD/Require.js来开发的任何项目),你都应该把一组功能单独的来形成不同的modules, 一般来说,应该把每一个module单独的放在一个file中。这就是分拆的概念,它让你或者是来读你代码的其他的人可以轻松的来明白你的代码是要做什么的

 

首先来建立第一个module,

把下面的代码放入app/modules/books.js

View Code
define(["namespace", "use!backbone"], function(namespace, Backbone){

    var Book = namespace.module();

    //Router

    Book.Router = Backbone.Router.extend({

        routes: {

            "book/:p" : "details"

        },

        details: function(hash) {

            var view = new Book.Views.Details({model: Library.get(hash)});

            view.render(function(el){

                $("#main").html(el);

            });

        }

    });

 

    //Instantiate Router

    var router = new Backbone.Router();

    //Book Model

    Book.Model = Backbone.Model.extend({});

    //Book Collection

    Book.Collection = Backbone.Collection.extend({

        model : Book.Model

    });

    //This will fetch teh book template and render it

    Book.Views.Details = Backbone.View.extend({

        template: "app/templates/books/details.html",

        render: function(done) {

            var view = this;

            //Fetch the template, render it to the View element and call done.

            namespace.fetchTemplate(this.template, function(templ){

                view.el.innerHTML = tmpl(view.model.toJSON());

                if(_.isFunction(done)){

                    done(view.el);

                }

            });

        }

    });

 

    //This will fetch the book list template and render it

    Book.Views.List = Backbone.View.extend({

        template: "app/templates/books/list.html",

        render: function(done) {

            var view = this;

            namespace.fetchTemplate(this.template, function(tmpl){

                view.el.innerHTML = tmpl({books: view.model.toJSON()});

                if(_.isFunction(done)) {

                    done(view.el);

                }

            });

        }

    });

    //Required, return the module for AMD compliance

    return Book;

});

 

上面的代码看起来非常长,但是逻辑非常简单清晰:

 

AMD module定义

 define(["namespace", "use!backbone"], function(namespace, Backbone) {

 var Book = namespace.module();

 return Book;

});

这是一个非常标准的AMD定义module的方式。通过上面的代码会告诉module loader, 需要namespacebackbone这两个module,它们的路径已经在app/config.js中定义了,callback函数中,你定义了自己的module,并且在函数的尾部返回了Book

 

Module's Router

Book.Router = Backbone.Router.extend({});

var router = new Book.Router();

browser跳转到route中定义的路径,就会触发我们在其内所定义的函数,当我们实例化routes之后,它就会自动管理我们的router.

 

Module's Data

Book.Model = Backbone.Model.extend({});

Book.Collection = Backbone.Collection.extend({

  model: Book.Model

});

在其中,我们可以定义所需要的datalogic. 在后面的代码中我们将会实例化Book.Model来存储每一本书和它的一些属性(title, author, etc).  Book.Collection定义了一组相关的Book.Model, 我们可以在逻辑上理解为,这一组书有多少本。

 

你可以把任意的bussiness logic,通过extend放到任意的collection中,例如,你如果想创建一个function,可以根据作者来过滤book,我们可以有以下代码:

Book.Collection = Backbone.Collection.extend({

  model: Book.Model,

  filterByAuthor: function(author){

    return this.filter(function(book){

      return book.get('author') === author;

    });

  }

});

 underscore的任意方法都可以直接用到backbonecollection上。

 

同样我们也可以对models来进行相同的操作。最理想的操作方式是你要把所有的bussiness logic放到models中。

 

Module's Views

Book.Views.Details = Backbone.View.extend({

  template: "app/templates/books/details.html",

  render: function(done) {

    var view = this;

    // Fetch the template, render it to the View element and call done.

    namespace.fetchTemplate(this.template, function(tmpl) {

      view.el.innerHTML = tmpl(view.model.toJSON());

      if (_.isFunction(done)) {

        done(view.el);

      }

    });

  }

});

你的module中也许含有多个views, 在我们的示例中,我们有list view和一个details view. 每一个view都有它们各自的template, render() function, render中,我们将会调用fetchTemplate(这个函数定义在namespace.js)中,它会把结果赋值给viewinnerHTML当结束后还会执行callback function(done).

 

Templates:

In app/templates/books/list.html

<h1>Listing of Books</h1>

<ul>

  <% _.each(books, function(book){ %>

    <li><a href="book/<%= book.id %>"><%= book.title %></a></li>

  <% }); %>

</ul>

 

In app/templates/books/details.html

 

<h1><%= title %></h1>

<ul>

  <li><b>Author: </b><%= author %></li>

  <li><b>Year Published: </b><%= published %></li>

</ul>

<a href="/">Back to List</a>

我们会为不同的views来创建不同的template, list view, 我们将会迭代collection中的model并且用一个链接来到达每一本收的details view. details view中,我们可以显示每本书所包含的不同的数据。

 

请注意,当我们点击每个链接的时候,我们的页面并没有切换,在代码中我们也并没有用到 preventDefault(), 这是因为我们已经在app/main.js中调用了一个方法,只要我们没有在link中加入data-bypass="true",它就会自动调用preventDefault().

 

main.js中,替换下面的code:

require([

  "namespace",

  // Libs

  "jquery",

  "use!backbone",

  // Modules

  "modules/book"

],

function(namespace, $, Backbone, Book) {

  window.Library = new Book.Collection([

    { id: 1, title: "A Tale of Two Cities", author: "Charles Dickens", published: 1859 },

    { id: 2, title: "The Lord of the Rings", author: "J. R. R. Tolkien", published: 1954 },

    { id: 3, title: "The Hobbit", author: "J. R. R. Tolkien", published: 1937 },

    { id: 4, title: "And Then There Were None", author: "Agatha Christie", published: 1939 }

  ]);

  // Defining the application router, you can attach sub routers here.

  var Router = Backbone.Router.extend({

    routes: {

      "":   "index"

    },

    index: function(){

      var view = new Book.Views.List({collection: Library});

      view.render(function(el){

        $("#main").html(el);

      })

    }

  });

  // Everything after the Router stays the same

});

 

一般来说,你的server端将会提供一个数据API供你提取数据,在本例中,我们只是简单的把数据定义为一个变量。

 源码请在这里下载

posted @ 2012-11-16 18:07  moonreplace  阅读(949)  评论(0编辑  收藏  举报