基本原理

优质
小牛编辑
132浏览
2023-12-01

在这部分内容中我们将探索下像Backbone.js这类框架如何适应JavaScript应用架构。通常,开发者创建桌面和服务器类应用有丰富的设计模式供他们去选择,但是,在过去的仅仅几年中,这些模式已经应用到了客户端开发中。

在开始探索任何JavaScript框架之前,非常有必要先认知一下设计模式原理。

MVC,MVP和Backbone.js

设计模式可以解决通用开发问题,可以引导开发者给他们的应用增加组织架构。

设计模式之所以这么有用是因为它是众多开发者从反复实践中总结出来的经验。尽管10或20年前开发者们在他们的项目中都使用不同的语言来实现模式,但是从中有许多值得我们学习的地方。

在这部分章节中,我们将会回顾2种比较流行的模式——MVC和MVP。我们将会探索如何使用Backbone.js快速实现这2种模式的更多细节。

MVC

MVC(Model-View-Controller)是一种提倡通过分层来改进应用的设计模式。它强制通过第三个组件(Controller)来分离业务数据(Model),用户界面(View),控制器通常管理逻辑,用户输入,协调模型与视图间的通讯。这种模式最早是Trygve Reenskaug在Smalltaok-80(1979)中设计的,当初被称之为Model-View-Controller-Editor。1994,设计模式: 面向对象软件中可重用性元素 (GOF或者四人帮一书)中详细定义了MVC,这本书普及了它的应用。

Smalltalk-80 MVC

随着时间的推移MVC模式变得更加笨重,非常有必要去了解下它早的设计初衷。在70年代,图形用户界面并不多见。有一种方法叫Separated Presentation(表现分离),可以清晰的分离模仿现实世界概念(比如一张图片、一个人)的域对象和被渲染到用户屏幕的描述对象。

Smalltalk-80实现的MVC把这个概念贯彻的更深入,而且有目的性的把应用逻辑从用户界面中分离出来。它的观点是解耦应用这些部分也可以把模型重用到应用中其它的用户界面。这里有些非常有趣的关于Smalltalk-80’s MVC架构的事情:

  • 一个域元素被当做一个Model,而且用户界面(Views和Controllers)并不知道。
  • 表现是View和控制器所关心的,但并不仅有一个view和controller。每个要展现到屏幕的元素都需要有一个View-Controller的组合,所以它们并没有正真的被分离。
  • 在这个组合中,Controller扮演的是处理用户输入(比如键盘和鼠标点击事件)的角色,做些用户可感知的事情。
  • 它依赖观察者模式在Model变化的时候来更新View。

有时,当开发者知道数十年前观察者模式(现在通常在发布/订阅系统中应用)也是MVC架构的一部分的时候,他们非常的惊讶。在Smalltalk-80的MVC中,View和Controller都观察了Model:Model改变的时候,View则做出响应。一个简单的例子就是基于股票市场数据的应用——因为它要展示实时的信息,所以在Models中的数据有任何改变都 要在View中立即刷新显示。

Martin Fowler在过去的些年中在写关于MVC起源origins 方面做了很多杰出的工作。如果你有兴趣了解更多关于Smalltalk-80 MVC的信息,推荐你阅读他的相关成果。

我们所知道的MVC

我们回顾了下70年代,回到现在。MVC模式已经应用到了各种程序语言上。比如,Ruby on Rails就是Ruby语言上实现的一个MVC web框架。JavaScript现在也有很多MVC框架,包括AngularJS—— 一个提供HTML、JavaScript扩展和动态内容渲染支持的框架,当然还有Backbone.js。嵌套式代码难于维护,结构混乱,让我们来看看MVC模式可以让Javascript开发人员做些什么。

MVC由三个核心部分组成:

模型(Models)

模型管理的应用的数据。它们不仅要关心用户界面层,同时也体现了应用需要的数据结构。当一个Model改变的时候(比如被更新),它将立即通知它的观察者(比如View,后面会提到这个概念)做出对应的响应。

为了更清晰的理解model,假设我们有一个JavaScript做的todo应用。在这个todo app中,而每个待办事项都有一个自己的模型, 因为它代表着一个特定域的唯一数据。Todo的模型可能会有标题,是否完成这2个属性。一个特定的待办事项保存在一个model的实例中。这里有一个基于Backbone.js的Todo model的实现:

  var Todo = Backbone.Model.extend({
    //默认属性
    defaults: {
      title: '',
      completed: false
    }
  });

  //默认属性的todo实例
  var firstTodo = new Todo();

  console.log("Todo's default title: " + firstTodo.get('title')); // ""
  console.log("Todo's default status: " + firstTodo.get('completed')); // false

  firstTodo.set('title', 'Enjoy reading the book');
  console.log('Title changed: ' + firstTodo.get('title'));

  //指定数据的todo实例
  var secondTodo = new Todo({ title: 'Try this code in chrome console'});

  console.log("Second todo title: " + secondTodo.get('title'));
  console.log("Second todo status: " + secondTodo.get('completed'));

不同的框架都会内置model的功能,而且通常都支持属性的验证,就像一个model的鉴定器一样,属性代表了model的内容。在实际应用中使用model的时候通常我们还需要一种方式对model进行持久化保存。持久化保存可以让我们在对model进行编辑和更新的时候保存它的最新状态。比如在浏览器中使用本地存储,或者与数据库同步。

一个model有可能会有多个views观察者。假设我们的Todo model包含了一些元数据,比如计划日期,说明,需要重复的日期(如果是我们定期会做的事情)。一个开发人员可以创建一个view来展现所有这些属性,或者也可以创建3个单独的view来显示每个属性。关键是Todo model不需要关系这些view如何组织,它只要在需要的时候通知数据更新就可以了。后面我们会谈到view的更多细节。

对于现代MVC/MV*框架,提供一种模型组合的方法并不常见。在Backbone中,这些组合叫Collections(集合)。把模型组合来管理可以让我们编写应用逻辑时基于一个组合来通知,它包含了任何一个model的改变。这样也避免了手动去观察单个的model实例。

下面我们看下如何把Todo models分组成Backbone Collection(集合):

  var Todo = Backbone.Model.extend({
    //默认属性
    defaults: {
      title: '',
      completed: false
    }
  });

  var Todos = Backbone.Collection.extend({
    model: Todo,

    // 为简单起见在本书的第一个部分我们都用localStorage来保存todo项。
    localStorage: new Store('todos-backbone')

    // 后面会用到的REST API:
    // url: "/todos"

  });

  var firstTodo = new Todo({title:'Read whole book'});

  //传入一个model的数组来初始化一个collection
  var todos = new Todos([firstTodo]);
  console.log(todos.length);

  // 通过Collection的方法来创建一个model,创建的model也会包含在collection内部。
  todos.create({title:'Try out code examples'});
  console.log(todos.length);

  var thirdTodo = new Todo({title:'Make something cool'});

  // 往collection中添加model
  todos.add(thirdTodo);
  console.log(todos.length);

  // Collection把所有models保存在models属性中,它是一个数组。
  console.log(todos.models);

如果你看到较老的关于MVC的资料,可能会告诉你models也管理应用的状态。在JavaScript中,应用的状态有一个特殊的意义,通常指某个view或某个固定时间用户屏幕上子view的当前状态。状态这个词经常在单页应用里讨论到,状态这个概念是需要被模拟的。

视图(Views)

视图是model的可视化展现,展现一个被过滤出来的视图或者它们的当前状态。一个view通常观察一个model,在model改变的时候被通知,然后做相应的自我更新。设计模式相关的文献通常把view当做是哑巴,因为它对models和controllers的信息知道的有限。

用户与view进行交互,通常就是阅读或者编辑model的数据。比如,在我们的这个todo应用案例中,todo model的视图展现发生在显示所有todo项列表的界面里。每个tod都用一个标题和完成复选框渲染。Model的编辑发生在编辑界面,用户选中一个特定的todo可以在form表单中编辑它的title。

在MVC中,正真开始更新model是在Controllers里,我们简短地来分析下。

我们通过一个简单的例子来进一步探索下view。下面我们用一个function来创建一个单独的Todo view,接受一个model和一个controller的实例。

在view里定义了一个通用的方法render(),用JavaScript模板引擎 (Underscore渲染和更新photoModel的内容,内容被写入photoEl

photoModelrender()添加为它的一个订阅者(subscribers)回调。这样,通过观察者(Observer)模式就可以在model改变的时候触发view的更新。

你可能想知道对用户交互行为这里是如何处理的。当用户点击view中的任何一个元素时,该做什么并不是由view决定的。应该是Controller来做决定。在这个简单的示例中,通过给photoEl添加事件监听,然后把click事件委派给controller,同时把需要的model传过去。

这种架构的好处就是各个组件复杂各自的角色,来实现应用的功能。

var buildPhotoView = function( photoModel, photoController ){

    var base        = document.createElement('div'),
        photoEl     = document.createElement('div');

     base.appendChild(photoEl);

     var render= function(){
        // We use a templating library such as Underscore
        // templating which generates the HTML for our
        // photo entry
        photoEl.innerHTML = _.template('photoTemplate', {src: photoModel.getSrc()});
     }

     photoModel.addSubscriber( render );

     photoEl.addEventListener('click', function(){
        photoController.handleEvent('click', photoModel );
     });

     var show = function(){
        photoEl.style.display  = '';
     }

     var hide = function(){
        photoEl.style.display  = 'none';
     }


     return{
        showView: show,
        hideView: hide
     }

}

模板

在支持MVC/MV*的JavaScript框架中,非常值得近距离的去审视下JavaScript模板和View之间的关系。

长期实践证明通过手工拼接字符串来创建大块的HTML片段是非常低效的。使用这种方式的开发者们经常会发现,他们遍历自己的数据,包裹在嵌套的div里面,然后使用过时的技术,比如document.write把所谓的’template’插入到DOM中。这种方式意味着必须使用标准的标签,脚本代码要放在页面内,而且很快就会变得难以阅读和维护,特别是对于构建大的应用来说。

JavaScript模板库(比如Handlebars.js or Mustache)通常用于view中定义模板,在HMTL标签中包含了一些模板变量。这些模板块可以保存在外部也可以保存在自定义类型(比如’text/template’)的script标签里。变量通过变量语法(比如{{name}})来定义。Javascript 模板库通常接受JSON的数据,并且往模板中填充数据这种繁重的工作也由框架自身来完成。使用模板库有非常多的好处,特别是当模板存在在外部时,应用可以根据需要动态的加载模板。

让我们来比较下2个HTML模板的列子。一个使用流行的Handlebars.js库实现,另一个使用Underscore的’microtemplates’。

Handlebars.js:

<li class="photo">
  <h2>{{caption}}</h2>
  <img class="source" src="{{src}}"/>
  <div class="meta-data">
    {{metadata}}
  </div>
</li>

Underscore.js Microtemplates:

<li class="photo">
  <h2><%= caption %></h2>
  <img class="source" src="<%= src %>"/>
  <div class="meta-data">
    <%= metadata %>
  </div>
</li>

在Microtemplates中,你也可以使用双大括号(比如{{}}) (或者其它你认为爽的字符)。使用大括号的话,可以向下面这样设置Underscore的templateSettings 属性:

_.templateSettings = { interpolate : /\{\{(.+?)\}\}/g };

关于导航和状态的注意事项

值得关注的是,在传统web开发中,在独立的view之间导航需要刷新页面。而在单页应用中,通过ajax从服务器端获取数据,可以在同一个页面里动态的渲染一个新的view,因为不会自动更新URL,导航的角色就落到了router(路由)的身上,路由独立的管理应用状态(比如允许用户收藏一个他们浏览过的view)。然而,路由并不是MVC或者类MVC框架的一部分,所以在这部分我并不打算介绍更多的细节。

控制器(Controllers)

controller是在model和view之间的媒介人,通常完成两件事:model改变时更新view和用户修改view时更新model。

在我们这个图片库应用中,控制器要处理在图片编辑视图里用户触发的改变,当用户完成编辑之后要更新photo model。

大部分JavaScript MVC框架都把controller从MVC模式中分离出来单独说明。在我看来,这种变化的原因,可能Javascript框架作者最初参照了服务端MVC的观念(比如Ruby on Rails)。认识到这种方式跟服务器端的并不完全一样,而且MVC中的C在服务器端也不是用于解决管理状态问题。这是一个非常聪明的途径,但对于初学者来说更难以理解传统MVC模式和无Javascript框架中controller的特定意义。

那Backbone.js有Controller吗?并不真正有。Backbone的Views通常包含了controller的逻辑,而且Routers(后面会讨论)也用于帮助管理应用状态,但这两者都不是传统MVC模式中真正意义上的控制器。

在这方面,与官方文档或者网络博客中描述的相反,Backbone并不是正真的MVC/MVP或者MVVM框架。事实上,它更适合归类到MV家族中,有自己的实现架构。当然这并没有什么不对,只是帮助你区分和理解传统MVC与你在Backbone项目中的MV

Spine.js与Backbone.js中的Controllers对比

Spine.js

现在我们知道controllers负责当model变化时更新view(同样也在用户改变view时更新model)。因为Backbone没有它自己明确的controllers,所以可以通过回顾controller来对比它和其它MVC框架的实现差异。我们先来看下Spine.js:

在这个例子中,我们会有一个控制器PhotosController,在应用中负责管理个人照片。会确保view更新时(例如用户编辑照片的元数据)相应的model也会更行。

(提示: 在这个列子中我们并不打算深入的研究Spine.js,但是非常值得通过它去学习更多关于Javascript框架的东西。)

//在Spine中通过继承Spine.Controller来创建Controllers

var PhotosController = Spine.Controller.sub({
  init: function(){
    this.item.bind("update", this.proxy(this.render));
    this.item.bind("destroy", this.proxy(this.remove));
  },

  render: function(){
    // Handle templating
    this.replace($("#photoTemplate").tmpl(this.item));
    return this;
  },

  remove: function(){
    this.$el.remove();
    this.release();
  }
});

在Spine中,controllers被认为是应用的胶水,添加和响应DOM事件,渲染模板,保持views和models的同步(在上下文环境下我们才能判断是一个控制器)。

在上面这个例子中,把render()remove()分别绑定给updatedestroy事件。当一个照片实例被更新,根据元数据重新渲染view。同样,如果照片从库中被删除,就从view中移除。如果你想知道上面代码段中的tmpl()函数:在render()函数中,我们用它渲染一段JavaScript模板#photoTemplate,返回一段HTML字符串用于替换控制器的当前元素。

它给我们提供了一种轻量,简单的方式来管理model和view之间的变化。

Backbone.js

在这节的后面我们还会回顾一下Backbone与传统MVC之间的区别,但是现在先让我们来关注下controllers。

在Backbone中,controller的逻辑在Backbone.View和Backbone.Router之间共同存在。在Backbone的早期版本中有Backbone.Controlle这个东西,但是后来被重新定义成Router,更清晰的体现了它的角色。

Router的主要目的就是把URL映射到应用对应的状态。当一个用户浏览到www.example.com/photos/42时,Router就会用于根据这个ID展现图片,并且定义对这个请求应该返回哪些行为控制。Routers可以包含传统的控制行为,比如绑定models和views之间的行为,或者渲染view中的某一部分。但是,Backbone的贡献者Tim Branyen指出,不用Backbone.Router也可以做这些事情。这样看来,一个使用Router的范例可能会是这样:

var PhotoRouter = Backbone.Router.extend({
  routes: { "photos/:id": "route" },

  route: function(id) {
    var item = photoCollection.get(id);
    var view = new PhotoView({ model: item });

    something.html( view.render().el );
  }
}):

MVC给我们带来了什么?

总结来说,MVC中的分离促进了应用功能实现的模块化,而且可以: * 更易于维护。当应用需要升级时可以非常清是否是数据相关的,如果是数据相关的可能要修改models或者controllers,如果是视觉相关的可能就要修改views。 * 解耦models和views, 意味着为业务逻辑编写单元测试页更简明。 * 避免在应用中复制model和controller代码这种低级行为。 * 如果应用规模较大分角色来开发的话,这种模块化可以让负责核心逻辑和界面的开发人员同时工作。

深入探索

现在,相信你对MVC模式已经有基本的了解了。为了满足大家的求知欲,这里我们将探索的更深入一点。

GoF (Gang of Four,四人组, 《Design Patterns: Elements of Reusable Object-Oriented Software》/《设计模式》一书的作者:Erich Gamma、Richard Helm、Ralph Johnson、John Vlissides)并没有把MVC提及为一种设计模式,而是把它当做一组用于构建用户界面的类集合。在他们看来,它其实是其它三个经典的设计模式的演变:观察者模式(Observer)(Pub/Sub), 策略模式(Strategy)和组合模式(Composite)。根据MVC在框架中的实现不同可能还会用到工厂模式(Factory)和装饰器(Decorator)模式。我在另一本免费的书JavaScript Design Patterns For Beginners中讲述了这些模式,如果你有兴趣可以阅读更多信息。

正如我们所讨论的,models表示应用的数据,而views处理屏幕上展现给用户的内容。为此,MVC在核心通讯上基于推送/订阅模型(惊讶的是在很多关于MVC的文章中并没有提及到)。当一个model变化时它对应用其它模块发出更新通知(publishes),订阅者(subscriber)——通常是一个Controller,然后更新对应的view。观察者——这种自然的观察关系促进了多个view关联到同一个model。

对于感兴趣的开发人员想更多的了解解耦性的MVC(根据不同的实现),这种模式的目标之一就是在一个主题和它的观察者之间建立一对多的关系。当这个主题改变的时候,它的观察者也会得到更新。Views和controllers的关系稍微有点不同。Controllers帮助views对不同用户的输入做不同的响应,是一个非常好的策略模式列子。

总结

已经回顾了经典的MVC模式,你现在应该明白了它是如何让开发者将一个应用清晰的分离开来。你应该也能区分出JavaScript MVC框架可能在实现上与原有模式的相似与不同。

当评定一个新的JavaScript MVC/MV*框架时,请记住——可以退一步想,看看它是如何实现Models, Views, Controllers或者其它备选方案,这样或许更能帮助你理解这个框架。

MVP

模型-视图-提供者(Model-view-presenter)(MVP)是一种MVC涉及模式的衍生品,更关注于表现逻辑。它是由一个叫Taligent的公司早在1990年代,当他们在使用一个C++ CommonPoint环境下模型时发起的。虽然MVC和MVP的目的都是将多重组件分离,但是它们有些基本原理上的不同。

在这个概要里我们将关注更适合基于web架构的MVP版本。

Models, Views & Presenters(提供者)

MVP中的P代表presenter(提供者)。它是包含为view提供用户界面逻辑的组件。不同于MVC,对view的调用被委派给了presenter,它是从view上分离出来的,代替view与用户界面通讯。这使得在单元测试中可以做很多有用的事情,比如伪造views。

大部分通常的MVP实现都是使用被动视图(Passive View,一种对所有活动组件和用途沉默的view),几乎没有逻辑。MVP的models基本上跟MVC的models一样处理应用数据。presenter也更MVC中的Controllers一样也负责与view和model通讯。正如你所猜测的,Presenters是MVP模式的核心, 包含了在views之上的表现逻辑。

通过view,presenters处理所有的用户请求,然后把所有数据传回。在这方面,它们取回数据,处理数据并且决定数据如何在view中展现。 在一些实现中,presenter同样与服务端交互,完成数据(models)的存储。Models可能会触发事件,但订阅事件并且通知view更新是presenter要做的。在这种被动式的架构中,没有直接进行数据绑定的概念。Views提供设置器( setters)给presenters来设置数据。

这种从MVC的转变的好处就是增加了应用的可测试性,而且在view和model之间有了跟清晰的分离。但是在这种模式中缺少对数据绑定的支持,意味着要为这种分离付出额外的成本。

尽管被动view(Passive View)也开放view接口的一种实现,但是它是一种变种,包括对事件的使用可以把Presenter与view进一步的解耦。因在JavaScript中没有接口结构,这里我们更多的把它当做一种协议而不是接口。但是技术上它还是一种API,从这个角度来看我们把它当做接口可能更准确。

同样,有一种监督控制器(Supervising Controller),更接近于MVC和MVVM模式,因为它从model和view提供直接的数据绑定。Key-value observing (KVO)插件(比如Derick Bailey’s Backbone.ModelBinding插件)把这种Supervising Controller概念引入了Backbone中。

MVP还是MVC?

MVP通常用于需要尽可能多复用逻辑的企业级应用中。有复杂views和大量用户交互的应用可能会发现MVC不是非常适合解决类问题,因为过多的依赖于多个控制器。而在MVP中,所有这些复杂的逻辑可以封装在presenter中,更容易维护。

因为MVP的views是通过接口来定义的,而且这个接口从技术上将是系统和view之间的唯一连接点(presenter除外),这种模式同样允许开发者编写表现逻辑而无需等待设计师提供页面布局实现。

根据实现,MVP比MVC也更容易实现自动化单元测试。原因就是presenter可以作为完整的用户接口模拟,因而可以依赖其它组件进行单元测试。根据我的经验还还要依赖于MVP在何种语言上实现(选择MVP用于JavaScript项目跟ASP.NET比起来有非常大的不同)。

过一天之后,你对MVC的理解可能跟MVP差不多,因为他们的区别主要在于语义上。只要你清晰的把应用核心分解成models, views和controllers(或者presenters),你就可以享受大部分的益处,而不用在意你选择哪种模式。

MVC, MVP和Backbone.js

有极少数情况,如果有JavaScript框架宣称他们用传统的架构来实现MVC或者MVP,很多JavaScript开发者也不认为它们与MVC和MVP是互相排斥的(实际上我们看到在web开发框架中比如ASP.NET或者GWT,MVP被严格的实现)。这可能是因为它必须在应用中增加presenter/view的逻辑,而且仍然认为是一种MVC风格。

Backbone的贡献者Irene Ros 赞同这种方式,当她考虑把Backbone的views分割 成她们自己独特的组件,她需要一些东西来组装它们。这可以是一个控制器路由(比如Backbone.Router,后面会讲到)或者在数据请求回来之后的一个回调。

那些觉得与MVC相比Backbone.js更符合MVP定义的开发者认为:

  • MVP的presenter比controller更好的描述了Backbone.View (介于View模板和数据绑定间的层) 。
  • model适合描述Backbone.Model (跟传统MVC的Model没啥差别)。
  • views最好的体现了模板(比如Handlebars/Mustache标记模板)。

对此的回应可能会是view也可以紧紧是一个view(按照MVC),因为Backbone足够灵活让它可以多用途。MVC中的V和MVP中的都可以用Backbone.View完成, 因为它可以支持2种方式:渲染原子组件和组装通过其它view来渲染的组件。

同时我们也可以看出来,Backbone中controller的功能由Backbone.View和Backbone.Router共同完成,在下面这个例子中也可以看出来这方面确实存在。

this.model.on('change',...)这一行,Backbone 的PhotoView用观察者模式订阅(subscribe)model的变化来更新view。同时在render()方法中也处理模板渲染,但是不同于其它的实现,用户交互同样也在view中处理(看events)。

var PhotoView = Backbone.View.extend({

    //... 标签列表
    tagName:  "li",

    // 传入模板并缓存
    template: _.template($('#photo-template').html()),

    // 指定DOM的事件
    events: {
      "click img" : "toggleViewed"
    },

    // PhotoView监听model的变化,以便重新渲染。
    //因为在这个app中**Photo** and a **PhotoView**是一对一的关系,
    //为方便起见这里直接对model进行引用

    initialize: function() {
      _.bindAll(this, 'render');
      this.model.on('change', this.render);
      this.model.on('destroy', this.remove);
    },

    // 渲染photo对象
    render: function() {
      this.$el.html(this.template(this.model.toJSON()));
      return this;
    },

    // 切换model`"viewed"`状态
    toggleViewed: function() {
      this.model.viewed();
    }

});

另一种不同的观点是,Backbone更类似我们前面提到的Smalltalk-80 MVC

正如Backbone忠实用户Derick Bailey所写,最好不要强制Backbone去适应某种设计模式。设计模式可以灵活的引导我们如何构建应用,但在这方便,Backbone都不是完美的符合MVC或者MVP。反而,它给我们带来了通过多重架构模式创建稳定框架的更好的概念。我们可以称之为Backbone式,MV*或者任何有帮助解释它别有风味的应用架构的定义。

这种概念如何起源非常值得弄清除,所以希望我对MVC和MVP的解释对你有帮助。大部分JavaScript框架的结构采用传统模式的路子,无论是有意还是无意的,重要的是他们都可以帮助我们开发更有组织,清晰,易维护的应用。

背景资料

Backbone.js

  • 核心资料:Model, View, Collection, Router。采用自己的MV*风格。
  • 良好的文档,而且一直在改进中。
  • 被大公司用于伟大的应用,比如SoundCloud、Foursquare。
  • views与models之间基于事件驱动通讯。正如我们所看到的,它直接给每个mode每个属性添加事件监听,可以让开发者更细粒度的控制view的改变。
  • 支持通过自定义事件,或者单独的Key-value observing (KVO) 库进行数据绑定。
  • 大力支持RESTful接口,所以models可以轻易的与后端关联。
  • 可扩展的事件系统。在Backbone中可以非常精细的支持pub/sub。
  • 原型通过new关键字来实例化,很多开发者更喜欢这种方式。
  • 模板框架无关性,不过默认提供了Underscore的micro-templating。Backbone可以与其他模板框架一起使用比如Handlebars。
  • 不支持嵌套的models,但是有插件可以帮你解决。
  • 为构建应用提供清晰和灵活的约定。Backbone不强制使用它的所有组件,有必须的组件就能正常工作。