Ember 觀察者

2018-12-11 14:46 更新

Ember可以檢測任何屬性的變化,包括計算屬性。

觀察者使用

Ember可以察覺所有屬性的變化,包括計算屬性。觀察者是非常有用的,特別是計算屬性綁定之后需要同步的時候。 觀察者經(jīng)常被Ember開發(fā)過度使用。Ember框架本身已經(jīng)大量使用觀察者,但是對于大多數(shù)的開發(fā)者面對開發(fā)問題時使用計算屬性是更適合的解決方案。 使用方式:可以用Ember.observer創(chuàng)建一個對象為觀察者。

// Observer對于Emberjs來說非常重要,前面你看到的很多代碼都是與它有關系,計算屬性之所以能更新也是因為它
Person = Ember.Object.extend({
  firstName: null,
  lastName: null,


  fullName: Ember.computed('firstName', 'lastName', function() {
    return this.get('firstName') + " " + this.get('lastName');
  }),


  //  當fullName被改變的時候觸發(fā)觀察者
  fullNameChange: Ember.observer('fullName', function() {
    console.log("The fullName is changed by caller");
    //return this.get('fullName');
  })
});


var person = Person.create({
  firstName: 'chen',
  lastName: 'ubuntuvim'
});
// 如果被觀察的計算屬性還沒執(zhí)行過get()方法不會觸發(fā)觀察者
console.log('fullName = ' + person.get('fullName'));  
//  fullName是依賴firstName和lastName的,這里改變了firstName的值,計算屬性會自動更新,
//  fullName被改變了所以會觸發(fā)觀察者
person.set('firstName', 'change firstName value');  // 觀察者會被觸發(fā)
console.log('fullName = ' + person.get('fullName'));

fullName是依賴firstNamelastName的,調(diào)用set()方法改變了firstName的值,自然的導致fullName的值也被改變了,fullName變化了就觸發(fā)觀察者。從執(zhí)行的結(jié)果就可以看出來;

運行結(jié)果圖

Ember還為開發(fā)者提供了另一種使用觀察者的方式。這種方式使你可以在類定義之外為某個計算屬性增加一個觀察者。

person.addObserver('fullName', function() {
    // deal with the change…
});

觀察者與異步

目前,觀察者在Ember中是同步的(不是筆誤,官網(wǎng)就是這么說的Observers in Ember are currently synchronous.)。這就意味著只要計算屬性一發(fā)生變化就會觸發(fā)觀察者。也因為這個原因很容易就會引入這樣的bug在計算屬性沒有同步的時候。比如下面的代碼;

Person.reopen({
  lastNameChanged: Ember.observer('lastName', function() {
    // The observer depends on lastName and so does fullName. Because observers
    // are synchronous, when this function is called the value of fullName is
    // not updated yet so this will log the old value of fullName
    console.log(this.get('fullName'));
  })
});

然而由于同步的原因如果你的的觀察者同時觀察多個屬性,就會導致觀察者執(zhí)行多次。

person = Ember.Object.extend({
  firstName: null,
  lastName: null,


  fullName: Ember.computed('firstName', 'lastName', function() {
    return this.get('firstName') + " " + this.get('lastName');
  }),


  //  當fullName被改變的時候觸發(fā)觀察者
  fullNameChange: Ember.observer('fullName', function() {
    console.log("The fullName is changed by caller");
    //return this.get('fullName');
  })
});
Person.reopen({
  partOfNameChanged: Ember.observer('firstName', 'lastName', function() {
    //  同時觀察了firstName和lastName兩個屬性
    console.log('========partOfNameChanged======');
  })
});
var person = Person.create({
  firstName: 'chen',
  lastName: 'ubuntuvim'
});


person.set('firstName', '[firstName]');
person.set('lastName', '[lastName]');

run result

顯然上述代碼執(zhí)行了兩次set()所以觀察者也會執(zhí)行2次,但是如果開發(fā)中需要設置只能執(zhí)行一次觀察出呢?Ember提供了一個once()方法,這個方法會在下一次循環(huán)所有綁定屬性都同步的時候執(zhí)行。

Person = Ember.Object.extend({
  firstName: null,
  lastName: null,


  fullName: Ember.computed('firstName', 'lastName', function() {
    return this.get('firstName') + " " + this.get('lastName');
  }),


  //  當fullName被改變的時候觸發(fā)觀察者
  fullNameChange: Ember.observer('fullName', function() {
    console.log("The fullName is changed by caller");
    //return this.get('fullName');
  })
});
Person.reopen({
  partOfNameChanged: Ember.observer('firstName', 'lastName', function() {
    //  同時觀察了firstName和lastName兩個屬性
    //  方法partOfNameChanged本身還是會執(zhí)行多次,但是方法processFullName只會執(zhí)行一次
    console.log('========partOfNameChanged======');  //  
    Ember.run.once(this, 'processFullName');
  }),
  processFullName: Ember.observer('fullName', function() {
    // 當你同時設置多個屬性的時候,此觀察者只會執(zhí)行一次,并且是發(fā)生在下一次所有屬性都被同步的時候
    console.log('fullName = ' + this.get('fullName'));
  })
});


var person = Person.create({
  firstName: 'chen',
  lastName: 'ubuntuvim'
});


person.set('firstName', '[firstName]');
person.set('lastName', '[lastName]');

run result

觀察者與對象初始化

觀察者一直到對象初始化完成之后才會執(zhí)行。 如果你想觀察者在對象初始化的時候就執(zhí)行你必須要手動調(diào)用Ember.on()方法。這個方法會在對象初始化之后就執(zhí)行。

Person = Ember.Object.extend({
  salutation:null,
  init() {
    this.set('salutation', 'hello');
    console.log('init....');
  },
  salutationDidChange: Ember.on('init', Ember.observer('salutation', function() {
    console.log('salutationDidChange......');
  }))
});


var p = Person.create();
p.get('salutationDidChange');  //  output > init....  salutationDidChange......
console.log(p.get('salutation'));  // output > hello
p.set('salutation');  //  output > salutationDidChange......

未獲取過值的計算屬性不會觸發(fā)觀察者

如果一個計算屬性從來沒有調(diào)用過get()方法獲取的其值,觀察者就不會被觸發(fā),即使是計算屬性的值發(fā)生變化了。你可以這么認為,觀察者是根據(jù)調(diào)用get()方法前后的值比較判斷出計算屬性值是否發(fā)生改變了。如果沒調(diào)用過get()之前的改變觀察者認為是沒有變化。 通常我們不需要擔心這個問題會影響到程序代碼,因為幾乎所有被觀察的計算屬性在觸發(fā)前都會執(zhí)行取值操作。如果你仍然擔心觀察者不會被觸發(fā),你可以在init()方法了執(zhí)行一次get操作。這樣足以保證你的觀察在觸發(fā)之前是執(zhí)行過get操作的。
對于初學者來說,屬性值的自動更新還是有點難以理解,到底它是怎么個更新法?。?!先別急,先放一放,隨著不斷深入學習你就會了解到這個是多么強大的特性。
博文完整代碼放在Github(博文經(jīng)過多次修改,博文上的代碼與github代碼可能又出入,不過影響不大?。绻阌X得博文對你有點用在github項目上給我個star吧。您的肯定對我來說是最大的動力??!

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號