Ember.js 入门指南 (二)

@(Ember)[MVVM|前端框架|HTML桌面应用]


序言

经常有人质疑,在前端搞MV*有什么意义?也有人跟我提出这样的疑问:以AngularJS,Knockout,BackBone为代表的MV*框架,它跟jQuery有什么区别?我jQuery用得好好的,有什么必要再引入这种框架?

其实,不管我们使用的是一个类库还是一个框架,都不应该忘记我们最终目的,或许你正在为一个项目做技术选型,或许你正在为你的应用考虑代码重构,又或许你只是单纯的想做一些学术性研究,所以框架和类库的选择没有绝对只有最适合。

以jQuery为代表,针对界面上常见的DOM操作,远程请求,数据处理等作了封装,也有专注于处理数据的Underscore,而今天的主角Ember.js正是一款为构建富HTML桌面应用的理想框架。


EMBER对象模型

Ember增强了简单的JavaScript对象模型,使之能够支持绑定和观察者,同时也支持一种更加强大的、基于混合的(mixin-based)代码共享途径。

作为最基本的形式,你可以使用Ember.Objectextend方法创建一个新的Ember类。

Person = Ember.Object.extend({
  say: function(thing) {
    alert(thing);
 }
});

一旦你成功创建了一个新类,就可以使用create来创建类的实例了。类中定义的任何属性在实例中都是可用的。

var person = Person.create();
person.say("Hello") // alerts "Hello"

创建实例时,也可以通过传入对象来为实例增添额外的属性。

var tom = Person.create({
  name: "Tom Dale",
  helloWorld: function() {
    this.say("Hi my name is " + this.get('name'));
  }
});
tom.helloWorld() // alerts "Hi my name is Tom Dale"

由于Ember支持绑定和观察者,因此你可以随时通过get方法访问属性,也可以通过set方法设置属性。

当创建一个对象的新的实例时,也可以覆写类中定义的任何属性和方法。在本例中,作为例子,你可以覆写从Person类继承的say方法。

var yehuda = Person.create({
  name: "Yehuda Katz",
  say: function(thing) {
    var name = this.get('name');
    this._super(name + " says: " + thing);
  }
});

你可以使用对象的_super方法(super是JavaScript中的保留字)来调用被你覆写的原始方法。

类的子类

你也可以使用extend方法为类创建子类。事实上,我们上面使用Ember.Object对象的extend方法创建新类时,即是创建了Ember.Object的子类。

var LoudPerson = Person.extend({
  say: function(thing) {
    this._super(thing.toUpperCase());
  }
});

当创建子类时,你可以使用this._super来调用被你覆写的方法。

重新打开类和实例

无需一次性将类定义完全,你可以使用reopen方法来重新打开(reopen)一个类并为其定义新的属性。

当使用reopen时,你也同样可以覆写已经存在的方法并调用this._super

Person.reopen({
  // override `say` to add an ! at the end
  say: function(thing) {
    this._super(thing + "!");
  }
});

正如你所见,reopen是用来为实例添加属性和方法的。而当你需要创建类的方法或为类本身添加属性时,则可使用reopenClass

Person.reopenClass({
  createMan: function() {
    return Person.create({isMan: true})
  }
});
Person.createMan().get('isMan') // true


计算属性 (Getters)

你可能经常需要一个基于其他属性计算而来的属性。Ember的对象模型可以使你很轻松地在常规的类定义中定义计算属性。

Person = Ember.Object.extend({
  // these will be supplied by `create`
  firstName: null,
  lastName: null,
  fullName: function() {
    var firstName = this.get('firstName');
    var lastName = this.get('lastName');
   return firstName + ' ' + lastName;
  }.property('firstName', 'lastName')
});
var tom = Person.create({
  firstName: "Tom",
  lastName: "Dale"
});
tom.get('fullName') // "Tom Dale"

当为类创建子类或者新的实例时,你可以覆写任何计算属性。

计算属性 (Setters)

你也可以定义当设置计算属性时Ember所执行的动作。当你尝试设置计算属性时,可以使用对应的名-值(key and value)对其进行设置。

Person = Ember.Object.extend({
  // these will be supplied by `create`
  firstName: null,
  lastName: null,
  fullName: function(key, value) {
    // getter
    if (arguments.length === 1) {
      var firstName = this.get('firstName');
      var lastName = this.get('lastName');
      return firstName + ' ' + lastName;
    // setter
    } else {
      var name = value.split(" ");
      this.set('firstName', name[0]);
      this.set('lastName', name[1]);
      return value;
    }
  }.property('firstName', 'lastName')
});
var person = Person.create();
person.set('fullName', "Peter Wagenet");
person.get('firstName') // Peter
person.get('lastName') // Wagenet

对于settergetterEmber都会调用计算属性,你可以通过检查参数的个数来确定该计算属性究竟是被getter还是setter调用的。

观察者

Ember支持观察任何属性,包括计算属性。你可以使用addObserver方法在某个对象上设置观察者。

Person = Ember.Object.extend({
  // these will be supplied by `create`
  firstName: null,
  lastName: null,
  fullName: function() {
    var firstName = this.get('firstName');
    var lastName = this.get('lastName');
    return firstName + ' ' + lastName;
  }.property('firstName', 'lastName')
});
var person = Person.create({
  firstName: "Yehuda",
  lastName: "Katz"
});
person.addObserver('fullName', function() {
  // deal with the change
});
person.set('firstName', "Brohuda"); // observer will fire

由于fullName这个计算属性依赖于firstName,所以更新firstName也会触发fullName上的观察者。

由于观察者的应用非常普遍,Ember提供了一种能够在类定义的内部定义观察者的方式。

Person.reopen({
  fullNameChanged: function() {
    // this is an inline version of .addObserver
  }.observes('fullName')
});


绑定

绑定在两个属性之间创建了一个连接,这样一个属性改变时另一个也可以随之自动更新到最新的值。绑定也可以连接同一对象内的属性,或者跨越两个不同的对象。与其他大部分框架所包含的绑定实现不同的是,Ember.js的绑定可以用在任何对象上,而不仅仅用在视图和模型之间。

最简单的创建双向绑定的方法是,创建一个以Binding字串结尾的属性,然后指定一个相对于全局作用域(global scope)的路径:

App.wife = Ember.Object.create({
  householdIncome: 80000
});
App.husband = Ember.Object.create({
  householdIncomeBinding: 'App.wife.householdIncome'
});
App.husband.get('householdIncome'); // 80000
// Someone gets raise.
App.husband.set('householdIncome', 90000);
App.wife.get('householdIncome'); // 90000

注意这个绑定不会立即更新,Ember会等待应用的所有代码运行完成之后再同步变更,因此你可以随意改变某个绑定的属性,而不用担心同步这些临时值所耗费的开销。

单向绑定

单向绑定只将变更单向传播。通常,单向绑定只是为了性能优化,你可以放心地使用更加简洁的双向绑定语法(当然,如果你总是只改变一边的话,那么双向绑定事实上也是单向绑定了)。

App.user = Ember.Object.create({
  fullName: "Kara Gates"
});
App.userView = Ember.View.create({
  userNameBinding: Ember.Binding.oneWay('App.user.fullName')
});
// Changing the name of the user object changes
// the value on the view.
App.user.set('fullName', "Krang Gates");
// App.userView.userName will become "Krang Gates"
// ...but changes to the view don't make it back to
// the object.
App.userView.set('userName', "Truckasaurus Gates");
App.user.get('fullName'); // "Krang Gates"


应用程序 APPLICATION

根据你的需求,有几种方式可以创建你的第一个Ember App.

如果你的需求较简单或者只是感兴趣随便玩玩,你可以下载Ember.js入门套件(Starter Kit),该入门套件基于HTML5 样板,无需任何构建工具、没有其他依赖。你也可以使用一些其他前端Scaffold工具来构建你的ember应用(例如: Yoeman )。当然你也可以根据自己的项目特点来构建自己的应用。


创建一个应用程序

每个Ember app都应该有一个Ember.Application的实例。这个对象将会作为你的app中所有类和实例的全局可访问的命名空间(globally-accessible namespace)。此外,它也在页面上设置了事件监听器,确保当用户与你的用户界面进行交互时你的视图可以接收到事件。

创建Ember.js应用程序的第一步是创建一个Ember.Application类的实例化对象:

window.App = Ember.Application.create();

这里将实例化的对象命名为App,开发者可以根据应用程序的用途,选择意义相符的名字。

创建一个应用程序的实例对象非常重要,原因如下:

  • 定义应用程序的命名空间,所有类都定义成该对象的属性(比如App.PostsView、App.PostsController)。这样做可以避免污染全局作用域。

  • 在document上增加事件监听,并负责将事件发送给视图。

  • 自动渲染模板,包括根模板,以及其他放入根模板的模板,都将被渲染。

  • 基于当前URL创建路由器并开始路由。

Person.reopenClass({
  createMan: function() {
    return Person.create({isMan: true})
  }
});
Person.createMan().get('isMan') // true

你可以随意为你的命名空间命名,不过必须以大写字母开头,这样绑定才能找到它。

如果要将Ember应用嵌入到某个现存的网站中,可以通过提供rootElement属性来将某个特定的元素作为事件监听器。


小结:

  • 使用计算属性创建由其他属性综合决定的新属性。

  • 计算属性不应该包含应用的行为,而且当调用时不应有任何附加影响。

  • 除非在极少数情况下,多次调用相同的计算属性应该总是返回同样的值(当然,除了属性所依赖的属性值改变的情况。)

  • 观察者应该包含反映其他属性变化的行为。

  • 当你需要在绑定完成同步之后执行一些行为的时候,观察者会非常有用。

  • 绑定通常用在确保位于不同层的对象总能保持同步。

  • 例如,你使用Handlebars绑定了你的视图和控制器,你也可能经常需要绑定同一层的两个对象。

  • 例如,你可能有个App.selectedContactController需要绑定在App.contactsControllerselectedContact属性上

你可能感兴趣的:(计算机编程)