Ember
可以檢測(cè)任何屬性的變化,包括計(jì)算屬性。
Ember
可以察覺所有屬性的變化,包括計(jì)算屬性。觀察者是非常有用的,特別是計(jì)算屬性綁定之后需要同步的時(shí)候。
觀察者經(jīng)常被Ember開發(fā)過度使用。Ember
框架本身已經(jīng)大量使用觀察者,但是對(duì)于大多數(shù)的開發(fā)者面對(duì)開發(fā)問題時(shí)使用計(jì)算屬性是更適合的解決方案。
使用方式:可以用Ember.observer
創(chuàng)建一個(gè)對(duì)象為觀察者。
// Observer對(duì)于Emberjs來說非常重要,前面你看到的很多代碼都是與它有關(guān)系,計(jì)算屬性之所以能更新也是因?yàn)樗?Person = Ember.Object.extend({
firstName: null,
lastName: null,
fullName: Ember.computed('firstName', 'lastName', function() {
return this.get('firstName') + " " + this.get('lastName');
}),
// 當(dāng)fullName被改變的時(shí)候觸發(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'
});
// 如果被觀察的計(jì)算屬性還沒執(zhí)行過get()方法不會(huì)觸發(fā)觀察者
console.log('fullName = ' + person.get('fullName'));
// fullName是依賴firstName和lastName的,這里改變了firstName的值,計(jì)算屬性會(huì)自動(dòng)更新,
// fullName被改變了所以會(huì)觸發(fā)觀察者
person.set('firstName', 'change firstName value'); // 觀察者會(huì)被觸發(fā)
console.log('fullName = ' + person.get('fullName'));
fullName
是依賴firstName
和lastName
的,調(diào)用set()
方法改變了firstName
的值,自然的導(dǎo)致fullName
的值也被改變了,fullName
變化了就觸發(fā)觀察者。從執(zhí)行的結(jié)果就可以看出來;
Ember
還為開發(fā)者提供了另一種使用觀察者的方式。這種方式使你可以在類定義之外為某個(gè)計(jì)算屬性增加一個(gè)觀察者。
person.addObserver('fullName', function() {
// deal with the change…
});
目前,觀察者在Ember
中是同步的(不是筆誤,官網(wǎng)就是這么說的Observers in Ember are currently synchronous.
)。這就意味著只要計(jì)算屬性一發(fā)生變化就會(huì)觸發(fā)觀察者。也因?yàn)檫@個(gè)原因很容易就會(huì)引入這樣的bug
在計(jì)算屬性沒有同步的時(shí)候。比如下面的代碼;
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'));
})
});
然而由于同步的原因如果你的的觀察者同時(shí)觀察多個(gè)屬性,就會(huì)導(dǎo)致觀察者執(zhí)行多次。
person = Ember.Object.extend({
firstName: null,
lastName: null,
fullName: Ember.computed('firstName', 'lastName', function() {
return this.get('firstName') + " " + this.get('lastName');
}),
// 當(dāng)fullName被改變的時(shí)候觸發(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() {
// 同時(shí)觀察了firstName和lastName兩個(gè)屬性
console.log('========partOfNameChanged======');
})
});
var person = Person.create({
firstName: 'chen',
lastName: 'ubuntuvim'
});
person.set('firstName', '[firstName]');
person.set('lastName', '[lastName]');
顯然上述代碼執(zhí)行了兩次set()
所以觀察者也會(huì)執(zhí)行2次,但是如果開發(fā)中需要設(shè)置只能執(zhí)行一次觀察出呢?Ember提供了一個(gè)once()
方法,這個(gè)方法會(huì)在下一次循環(huán)所有綁定屬性都同步的時(shí)候執(zhí)行。
Person = Ember.Object.extend({
firstName: null,
lastName: null,
fullName: Ember.computed('firstName', 'lastName', function() {
return this.get('firstName') + " " + this.get('lastName');
}),
// 當(dāng)fullName被改變的時(shí)候觸發(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() {
// 同時(shí)觀察了firstName和lastName兩個(gè)屬性
// 方法partOfNameChanged本身還是會(huì)執(zhí)行多次,但是方法processFullName只會(huì)執(zhí)行一次
console.log('========partOfNameChanged======'); //
Ember.run.once(this, 'processFullName');
}),
processFullName: Ember.observer('fullName', function() {
// 當(dāng)你同時(shí)設(shè)置多個(gè)屬性的時(shí)候,此觀察者只會(huì)執(zhí)行一次,并且是發(fā)生在下一次所有屬性都被同步的時(shí)候
console.log('fullName = ' + this.get('fullName'));
})
});
var person = Person.create({
firstName: 'chen',
lastName: 'ubuntuvim'
});
person.set('firstName', '[firstName]');
person.set('lastName', '[lastName]');
觀察者一直到對(duì)象初始化完成之后才會(huì)執(zhí)行。
如果你想觀察者在對(duì)象初始化的時(shí)候就執(zhí)行你必須要手動(dòng)調(diào)用Ember.on()
方法。這個(gè)方法會(huì)在對(duì)象初始化之后就執(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......
如果一個(gè)計(jì)算屬性從來沒有調(diào)用過get()
方法獲取的其值,觀察者就不會(huì)被觸發(fā),即使是計(jì)算屬性的值發(fā)生變化了。你可以這么認(rèn)為,觀察者是根據(jù)調(diào)用get()
方法前后的值比較判斷出計(jì)算屬性值是否發(fā)生改變了。如果沒調(diào)用過get()
之前的改變觀察者認(rèn)為是沒有變化。
通常我們不需要擔(dān)心這個(gè)問題會(huì)影響到程序代碼,因?yàn)閹缀跛斜挥^察的計(jì)算屬性在觸發(fā)前都會(huì)執(zhí)行取值操作。如果你仍然擔(dān)心觀察者不會(huì)被觸發(fā),你可以在init()
方法了執(zhí)行一次get
操作。這樣足以保證你的觀察在觸發(fā)之前是執(zhí)行過get操作的。
對(duì)于初學(xué)者來說,屬性值的自動(dòng)更新還是有點(diǎn)難以理解,到底它是怎么個(gè)更新法!?。∠葎e急,先放一放,隨著不斷深入學(xué)習(xí)你就會(huì)了解到這個(gè)是多么強(qiáng)大的特性。
博文完整代碼放在Github(博文經(jīng)過多次修改,博文上的代碼與github代碼可能又出入,不過影響不大?。?,如果你覺得博文對(duì)你有點(diǎn)用在github項(xiàng)目上給我個(gè)star
吧。您的肯定對(duì)我來說是最大的動(dòng)力??!
更多建議: