Ember 觀察者

2018-12-11 14:46 更新

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

觀察者使用

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

  1. // Observer對于Emberjs來說非常重要,前面你看到的很多代碼都是與它有關(guān)系,計算屬性之所以能更新也是因為它
  2. Person = Ember.Object.extend({
  3. firstName: null,
  4. lastName: null,
  5. fullName: Ember.computed('firstName', 'lastName', function() {
  6. return this.get('firstName') + " " + this.get('lastName');
  7. }),
  8. // 當fullName被改變的時候觸發(fā)觀察者
  9. fullNameChange: Ember.observer('fullName', function() {
  10. console.log("The fullName is changed by caller");
  11. //return this.get('fullName');
  12. })
  13. });
  14. var person = Person.create({
  15. firstName: 'chen',
  16. lastName: 'ubuntuvim'
  17. });
  18. // 如果被觀察的計算屬性還沒執(zhí)行過get()方法不會觸發(fā)觀察者
  19. console.log('fullName = ' + person.get('fullName'));
  20. // fullName是依賴firstName和lastName的,這里改變了firstName的值,計算屬性會自動更新,
  21. // fullName被改變了所以會觸發(fā)觀察者
  22. person.set('firstName', 'change firstName value'); // 觀察者會被觸發(fā)
  23. console.log('fullName = ' + person.get('fullName'));

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

運行結(jié)果圖

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

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

觀察者與異步

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

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

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

  1. person = Ember.Object.extend({
  2. firstName: null,
  3. lastName: null,
  4. fullName: Ember.computed('firstName', 'lastName', function() {
  5. return this.get('firstName') + " " + this.get('lastName');
  6. }),
  7. // 當fullName被改變的時候觸發(fā)觀察者
  8. fullNameChange: Ember.observer('fullName', function() {
  9. console.log("The fullName is changed by caller");
  10. //return this.get('fullName');
  11. })
  12. });
  13. Person.reopen({
  14. partOfNameChanged: Ember.observer('firstName', 'lastName', function() {
  15. // 同時觀察了firstName和lastName兩個屬性
  16. console.log('========partOfNameChanged======');
  17. })
  18. });
  19. var person = Person.create({
  20. firstName: 'chen',
  21. lastName: 'ubuntuvim'
  22. });
  23. person.set('firstName', '[firstName]');
  24. person.set('lastName', '[lastName]');

run result

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

  1. Person = Ember.Object.extend({
  2. firstName: null,
  3. lastName: null,
  4. fullName: Ember.computed('firstName', 'lastName', function() {
  5. return this.get('firstName') + " " + this.get('lastName');
  6. }),
  7. // 當fullName被改變的時候觸發(fā)觀察者
  8. fullNameChange: Ember.observer('fullName', function() {
  9. console.log("The fullName is changed by caller");
  10. //return this.get('fullName');
  11. })
  12. });
  13. Person.reopen({
  14. partOfNameChanged: Ember.observer('firstName', 'lastName', function() {
  15. // 同時觀察了firstName和lastName兩個屬性
  16. // 方法partOfNameChanged本身還是會執(zhí)行多次,但是方法processFullName只會執(zhí)行一次
  17. console.log('========partOfNameChanged======'); //
  18. Ember.run.once(this, 'processFullName');
  19. }),
  20. processFullName: Ember.observer('fullName', function() {
  21. // 當你同時設(shè)置多個屬性的時候,此觀察者只會執(zhí)行一次,并且是發(fā)生在下一次所有屬性都被同步的時候
  22. console.log('fullName = ' + this.get('fullName'));
  23. })
  24. });
  25. var person = Person.create({
  26. firstName: 'chen',
  27. lastName: 'ubuntuvim'
  28. });
  29. person.set('firstName', '[firstName]');
  30. person.set('lastName', '[lastName]');

run result

觀察者與對象初始化

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

  1. Person = Ember.Object.extend({
  2. salutation:null,
  3. init() {
  4. this.set('salutation', 'hello');
  5. console.log('init....');
  6. },
  7. salutationDidChange: Ember.on('init', Ember.observer('salutation', function() {
  8. console.log('salutationDidChange......');
  9. }))
  10. });
  11. var p = Person.create();
  12. p.get('salutationDidChange'); // output > init.... salutationDidChange......
  13. console.log(p.get('salutation')); // output > hello
  14. 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代碼可能又出入,不過影響不大?。?,如果你覺得博文對你有點用在github項目上給我個star吧。您的肯定對我來說是最大的動力?。?/p>

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

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號