`
usenrong
  • 浏览: 506017 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

Backbone JS框架指南

 
阅读更多
Backbone 是一个前端 JS 代码 MVC 框架,被著名的 37signals 用来构建他们的移动客户端。它不可取代 Jquery,不可取代现有的 template 库。而是和这些结合起来构建复杂的 web 前端交互应用。

如果项目涉及大量的 javascript 代码,实现很多复杂的前端交互功能,首先你会想到把数据和展示分离。使用 Jquery 的 selector 和 callback 可以轻松做到这点。但是对于富客户端的WEB应用大量代码的结构化组织非常必要。

Backbone 就提供了 javascript 代码的组织的功能。Backbone 主要包括 models, collections, views 和 events, controller 。

Models 用来创建数据,校验数据,存储数据到服务器端, Collections 包含你创建的 functions ,Views 用来展示数据。

Models 还可以绑定事件。比如用户动作变化触发 models 的 change 事件,所有展示此model 数据的 views 都会接收到 这个 change 事件,进行重绘。

事件的绑定和触发

以下是object 绑定 alert 事件和匿名回调函数的代码例子,object 之后 触发 alert 事件,并且传入参数 “an event”

var object = {};

_.extend(object, Backbone.Events);

object.bind("alert", function(msg) {
  alert("Triggered " + msg);
});

object.trigger("alert", "an event");
假如你的网页上事件很多也可以用proxy的方式来分发所有事件:

proxy.bind("all", function(eventName) {
  object.trigger(eventName);
});
Backbone 的 Models 是应用的核心

他包含了数据对象的属性,操作数据对象的函数。还实现和服务端交互的动作。

以下是定时从服务器端更新 channel 的数据:

// Poll every 10 seconds to keep the channel model up-to-date.
setInterval(function() {
  channel.fetch();
}, 10000);
以下是存储 book 的数据,这里通过重载 sync 函数,只让数据 alert 出来,sync 中的默认触发事件包括 fetch save refresh

Backbone.sync = function(method, model) {
  alert(method + ": " + JSON.stringify(model));
};

var book = new Backbone.Model({
  title: "The Rough Riders",
  author: "Theodore Roosevelt"
});

book.save();
Backbone 的 Controller 用来对 URL 和事件进行绑定

以下下例子中,分别将不同的以#开头的 URL 片段 绑定到不同的函数,实现服务器端 MVC 模型中的 router 一样的功能

var Workspace = Backbone.Controller.extend({

  routes: {
    "help":                 "help",    // #help
    "search/:query":        "search",  // #search/kiwis
    "search/:query/p:page": "search"   // #search/kiwis/p7
  },

  help: function() {
    ...
  },

  search: function(query, page) {
    ...
  }

});
值得提出的是 Backbone 的 router 也支持正则表达式的匹配

initialize: function(options) {

  // Matches #page/10, passing "10"
  this.route("page/:number", "page", function(number){ ... });

  // Matches /117-a/b/c/open, passing "117-a/b/c"
  this.route(/^(.*?)\/open$/, "open", function(id){ ... });

}
Backbone 的 Sync 默认通过调用Jquery的ajax方法来实现和服务器端的交互,实现数据的 CURD

比如 fetch 方法会触发 read 事件

Backbone 的 Views 用来接收用户的操作和修改 Model 的数据 ,另外通过 render 来展示数据

默认 render 并没有实现,你可以用 Mustache.js 或者 Underscore.js 来实现。

以下是接收用户操作的代码例子:

var DocumentView = Backbone.View.extend({

  events: {
    "dblclick"                : "open",
    "click .icon.doc"         : "select",
    "contextmenu .icon.doc"   : "showMenu",
    "click .show_notes"       : "toggleNotes",
    "click .title .lock"      : "editAccessLevel",
    "mouseover .title .date"  : "showTooltip"
  },

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

  open: function() {
    window.open(this.model.get("viewer_url"));
  },

  select: function() {
    this.model.set({selected: true});
  },

  ...

});
以下是数据渲染 render 的例子

var Bookmark = Backbone.View.extend({
  render: function() {
    $(this.el).html(this.template(this.model.toJSON()));
    return this;
  }
});
参考

http://documentcloud.github.com/backbone/

http://documentcloud.github.com/backbone/examples/todos/index.html

Also see:

CoffeeScript – 未来可以取代 JavaScript 的语言
3 Tips of linux uses and 1 tip of Jquery
jQuery初探
分享到:
评论
1 楼 usenrong 2012-01-16  
简而言之,Backbone.js是一个可以在前端组织MVC的javascript框架。

写的Javascript代码一旦多起来,没有一个好的组织,那就会像噩梦一样。

Backbone提供了Models, Collections, Views。Models 用来创建数据,校验数据,绑定事件,存储数据到服务器端;Collections 包含你创建的 functions;Views 用来展示数据。如此这般,在前端也做到了数据和显示分离。

Backbone依赖于Underscore.js,这是一个有很多常用函数的js文件(很好用)

下面简要介绍下Backbone的用法。

1. Backbone.Events

Events可以被添加到任何一个javascript对象中,一旦对象与Events合体,就可以自定义事件了。

var obj = {};
_.extend(obj, Backbone.Events);
obj.bind('data', function(data) {
alert('Receive Data: ' + data);
});
obj.trigger('data', 'This is Backbone.event');
obj.unbind('data');
obj.trigger('data', 'This is Backbone.event');
如上,一旦使用了_.extend(obj, Backbone.Events),obj对象就获得了自定义事件的能力。

obj.bind(event, callback) bind方法前面是事件名,后面是回调函数。bind可以将回调函数绑定到对象上,事件触发时便会执行回调函数。另外,如果事件很多,可以给事件加上命名空间,例如”poll:start”, “change:selection”。

obj.unbind([event], [callback]) unbind方法就是移除事件绑定,如果event, callback都没有,就将移除所有绑定。

obj.trigger(event,[*args]) 触发事件。

2. Backbone.Model
扩展了Backbone.Model类的对象就会有一些特殊的方法,用来更好地管理对象。通过Backbone.Model.extend(properties, [classProperties]) 扩展成Backbone.Model。第二个参数可选的。

首先,建立一个属于我们自己的Model类

var Book = Backbone.Model.extend({
defaults:{ //可有可无的,如果在实例化对象的时候,不更新属性值,将会现实default value
"title": "A book",
"author": "Jason",
"publish": "2011"
}
});
当我们创建一个Model的实例的时候,可以直接初始化属性并赋值;也可能通过重写initialize方法来初始化实例。

var book = new Book({
       ISBN: 1000
});
book.bind("change:title", function(model, title) {
  alert("Changed title from " + book.previous("title") + " to " + title);
});
var book1 = new Book;
//通常建议是用set来更新实例的属性
book.set({ title: "One Thousand and One Nights", author: "Scheherazade"});
alert(JSON.stringify(book));
alert(JSON.stringify(book1));
豆瓣说中的Backbone.Model一例

var User = Backbone.Model.extend({
    initialize: function() {
        this.url = "/api/users/" + this.get("uid");
        shuo.loggedIn && this.get("uid") === UID && this.set({
            i: !0
        })
    }
});
3. Backbone.Collection
Collection是model的有序集合。Collection里面包含了很多方法,可以非常方便地使用这些方法来对集合进行操作,包括add,remove,map,filter等等。

var b1 = new Book();
var b2 = b1.clone();
var b3 = b1.clone();
var Library = Backbone.Collection.extend({
model: Book
});
var books = new Library([b1,b2,b3]);
books.add([{title:"三重门",author:"韩寒"}]);
alert(JSON.stringify(books));
4. Backbone.Router(原来是Controller)
由于富Ajax的网站大多用带#的url来识别抓取服务器内容,因此Backbone也提供了前端的url#fragment的路由支持,并且可以把他们绑定到Action和Event中去。

注意:在使用前端路由的功能之前,一定要调用一次Backbone.history.start()或者Backbone.history.start({pushState: true})来初始化URL。

我们以豆瓣说的前端代码为例解说下这个功能。

http://img3.douban.com/anduin/anduin-min-1307608962.js

这堆代码的最后一个大块就是Router的使用。

这一块的大致结构如下:

App.Controllers.Statuses = Backbone.Controller.extend({
    routes: {
        "": "home",
        "!/comments": "comments",
        "!/mentions": "mentions",
        "!/:uid": "profile",
        "!/:uid/following": "following",
        "!/:uid/followers": "followers",
        "!/:uid/status/:id": "status",
        "!/search/users/:query": "user_search",
        "!/search/:query": "search"
    },
    initialize: function(){...}
    home: function(){...}
    comments: function() {...}
    mentions: function() {...}
    profile: function(a) {...}
    status: function(a, b) {...}
    following: function(a) {...}
    followers: function(a) {...}
    user_search: function(a) {...}
    search: function(a) {...}
});
这样我们就很容易看出Backbone.Router.extend(properties, [classProperties]) 的用法了。properties里面要有一个routes的哈希表,提供了路由和方法名的键值对。

所以我们看到http://shuo.douban.com/#!/comments对应着下面的comments方法。这个页面对应着“最新回复”模块。

我们还可以看到#fragment里面有!这个符号,这个是给搜索引擎识别用的(#原作者也没作解释)。

http://shuo.douban.com/#!/search/users/豆瓣

就对应着”!/search/users/:query”: “user_search”,这个路由,继而可以用user_search()来处理。

(特别的 “file/*path” 可以匹配#file/nested/folder/file.txt, 参数 “nested/folder/file.txt”会被传送到相对应的Action中去。)

顺便提一句,当url匹配后,会触发一个和Action名字有关的事件,比如”!/comments”: “comments”,如果访问了http://shuo.douban.com/#!/comments,就会触发”route:comments”的事件,因此我们可以用
controller.bind(“route:comments”, function(){…});来处理一些额外的逻辑。

initialize方法是构造器,初始化,可以加入自定义的相关逻辑。

initialize: function() {
        shuo.themes.loadDefaultTheme();
        shuo.loggedIn && (
        mMy = new User({ uid: UID }),
        mMyStat = new Stat,
        mMyFollowing = new App.Collections.Following([], {
            uid: UID,
            count: 9
        }),
         cHomeTimeline = new App.Collections.HomeTimeline([],
        {
            type: STREAM_TYPE_HOME_TIMELINE
        }),
        cSuggestions = new App.Collections.Suggestions([], {
            count: 10
        }),
        mMy.fetch(),
        mMyStat.fetch(),
        mMyFollowing.fetch())
    },
每当匹配了一个URL,页面首先先去除刚才的内容,加载默认的空内容,如果登录了豆瓣说则初始化User(登录用户),Stat(统计),Following(关注当前用户的用户),HomeTimeLine(时间线),Suggestion,然后用fetch获取数据。

最后,Controller还有route、saveLocation方法,this.route(“page/:number”, “page”, function(number){ … });这个方法可以很快的建立一个路由,this.saveLocation(“!/comments”);可以不触发hashchange时间的到达另外一个url。

5. Backbone.View
View并不操作html或者css,所有的操作留给了各种各样JS的模板库。
豆瓣说JS文件倒数的十几个块都是与Backbone.View有关的区块。

App.Views.UserInfo = Backbone.View.extend({
    model: User,
    className: "components cross",
    template: $("#user-info-template").html(),
    initialize: function() {
        _.bindAll(this, "render");
        this.model.bind("change", this.render)
    },
    render: function() {
        var a = this.model;
        $(this.el).html(Mustache.to_html(this.template, a.toJSON()));
        $(this.el).find(".days").html(function() {
            var b = a.get("created_at");
            return getDays(b)
        });
        return this
    }
});
在这里,最外层用了Backbone.View.extend(properties, [classProperties]),在initialize中一旦User类触发了change事件就会执行render方法,继而显示新的视图。

render方法中总是有个约定俗称的写法的。this.el是一个DOM对象,render的目的就是把内容填到this.el中。this.el会根据view提供的tagName, className, id属性创建,如果一个都没有,就会创建一个空的DIV。

更新完this.el后,我们还应该return this;这样才能继续执行下面的链式调用(如果有的话)。

豆瓣说用jquery+mustache来显示,事实上我们还可以采用haml-js,eco等等模板,js模板在GITHUB上也是多如牛毛的。

我们也可以用$(view.el).remove()或者view.remove()很方便的清空DOM。

View层有一个委托事件的机制。直接看代码:

App.Views.Likers = Backbone.View.extend({
    className: "likers-manager",
    template: $("#likers-components-template").html(),
    title_template: "{{#like_count}}
{{like_count}}/u4eba/u8d5e

{{/like_count}}",
    events: {
        "click .btn-more": "loadMore"
    },
    initialize: function() {
        _.bindAll(this, "render", "updateTitle", "loadOne", "loadAll", "loadMore");
        ...
    }
    render: function() { ... }
    updateTitle: function() { ... }
    loadOne: function(a) { ... }
    loadAll: function() { ... }
    loadMore: function(a) { ... }
});
在这里面有个events的键值对,格式为{"event selector": "callback"},其中click为事

相关推荐

Global site tag (gtag.js) - Google Analytics