今天漠吻,我們要聊得是Angularjs中的小明星$apply
。當(dāng)我們數(shù)據(jù)更新了司恳,但是view層卻沒反應(yīng)時途乃,總能聽到有人說,用apply吧扔傅,然后欺劳,懵懂無知的我們,在賦值代碼后面加了$scope.$apply()
,然后就驚喜的發(fā)現(xiàn)铅鲤。噢划提,真的更新了。
然而邢享,有些時候鹏往,編譯器會無情的給你返回
Error: $digest already in progress
那么,導(dǎo)致這些現(xiàn)象的原因時什么的呢骇塘?$apply
究竟干了啥伊履?聽我慢慢道來。
一.$apply的作用
$apply()函數(shù)可以從Angular框架的外部讓表達(dá)式在Angular上下文內(nèi)部執(zhí)行款违。
上面是AngularJs權(quán)威教程中的一句話唐瀑。什么意思呢?
首先插爹,你要清楚哄辣,在原生js或者第三方框架下请梢,修改model,是有可能不會觸發(fā)視圖更新的力穗,比如setTimeout毅弧、jquery插件。為什么当窗?因?yàn)樗麄兠撾x了Angularjs的上下文够坐,Angularjs并不能監(jiān)聽到數(shù)據(jù)的改變⊙旅妫看例子元咙。
1.setTimeout
html:
<p>{{name}}</p>
js:
$scope.name="張三";
setTimeout(function(){
$scope.name = '李四';
//$scope.$apply()
},500)
首先,name等于張三巫员,500ms后蛾坯,我把他賦值為李四,但是疏遏,頁面上并沒有改變脉课,依然是張三。
而财异,我們把$scope.$apply()
放開倘零,就正常了,張三成功變?yōu)槔钏摹?/p>
2.第三方插件
html:
<p>Date: <input type="text" id="datepicker"></p>
<p>
<header>所選日期</header>
{{selectedDate}}
</p>
js:
$scope.selectedDate = '';
$( function() {
$( "#datepicker" ).datepicker({
onClose: function( selectedDate ) {
$scope.selectedDate = selectedDate;
// $scope.$apply();
}
});
} );
這是jquery的datepicker插件戳寸,當(dāng)我們選定日期后呈驶,下面的日期應(yīng)該隨之顯現(xiàn),而現(xiàn)在卻沒有疫鹊。這種情況就必須依靠$apply()
,才能更新視圖袖瞻。
以上兩種情況,都因?yàn)椴惶幱贏ngularjs上下文中拆吆,導(dǎo)致監(jiān)聽不到數(shù)據(jù)的變化聋迎。而$apply
究竟干了什么,才導(dǎo)致數(shù)據(jù)更新正常了呢枣耀?
其實(shí)$apply相當(dāng)于一個觸發(fā)器霉晕,它的作用就是觸發(fā)digest循環(huán),從而更新視圖捞奕。
在digest是Angularjs的核心牺堰,是它實(shí)現(xiàn)了神奇的數(shù)據(jù)綁定。凡是觸發(fā)事件颅围,必會觸發(fā)digest循環(huán)伟葫,比如,我們數(shù)值的ng事件院促,click啊筏养,change,實(shí)際上都是觸發(fā)了digest循環(huán)斧抱。
所以,我們所做的事撼玄,其實(shí)就是手動觸發(fā)了digest循環(huán)夺姑。關(guān)于digest循環(huán)墩邀,屬于題外話掌猛,這里不做過多介紹,想深入了解的同學(xué)眉睹,可以看看書籍荔茬,或者百度。
二.更好地運(yùn)用digest循環(huán)
在Angularjs中竹海,除了$apply
可以觸發(fā)digest循環(huán)外慕蔚,還有其他的方法,也可以觸發(fā)此循環(huán)斋配。而且$apply
往往時最壞的選擇孔飒。下面推薦一些更好的選擇。
1.$digest
$scope.$digest()
的速度要比$apply
要快艰争,因?yàn)樗桓庐?dāng)前作用域和子作用域的值坏瞄,對于父作用域時不管的。而$apply
還要評估父作用域甩卓,這就大大消耗了性能鸠匀。
2.$timeout
用$timeout
去代替你的setTimeout,$timeout
作為Angularjs的自帶服務(wù),當(dāng)然時更契合Angularjs環(huán)境啦逾柿。它會隱性觸發(fā)digest循環(huán)缀棍,而且它會延遲執(zhí)行,會在上一個digest循環(huán)完成后的下一刻机错,觸發(fā)digest循環(huán)爬范,這樣就不會出現(xiàn)上文所說的
$digest already in progress
我們把setTime的代碼放到$timeout
中
$timeout(function(){
$scope.name = '李四';
},500)
這就能正常工作了,看弱匪,沒有討厭的apply了坦敌!
3.$evalAsync
最推薦的應(yīng)該時這個方法了。如果當(dāng)前正好有一個digest循環(huán)在執(zhí)行痢法,那么它就會把導(dǎo)致digest循環(huán)的操作狱窘,放到當(dāng)前digest循環(huán)中去執(zhí)行。而$timeout
是要等到當(dāng)前digest循環(huán)執(zhí)行完财搁,再執(zhí)行一次digest循環(huán)才可以蘸炸。所以evalAsync執(zhí)行更快,性能更好尖奔。我們可以像$timeout
那樣去調(diào)用它搭儒,即
$scope.$evalAsync(
function( $scope ) {
console.log( "$evalAsync" );
}
);
以上穷当,就是今天要說的全部內(nèi)容。Angularjs中還藏著許多奧秘淹禾,和更好的使用方法馁菜,希望大家可以深入地研究,分享出更好的文章铃岔。
下面是可執(zhí)行的代碼汪疮,大家可以探究探究
https://codepen.io/hanwolfxue/pen/yEZbYQ