Angularjs中的$apply及優(yōu)化使用詳解
前言
對(duì)于一個(gè)在前端屬于純新手的我來(lái)說(shuō),Javascript都還是一知半解,要想直接上手angular JS,遇到的阻力還真是不少。不過(guò)我相信,只要下功夫,即使是反人類的設(shè)計(jì)也不是什么大的問(wèn)題。
今天,我們要聊得是Angularjs中的小明星$apply。當(dāng)我們數(shù)據(jù)更新了,但是view層卻沒(méi)反應(yīng)時(shí),總能聽(tīng)到有人說(shuō),用apply吧,然后,懵懂無(wú)知的我們,在賦值代碼后面加了$scope.$apply() ,然后就驚喜的發(fā)現(xiàn)。噢,真的更新了。
然而,有些時(shí)候,編譯器會(huì)無(wú)情的給你返回
Error: $digest already in progress
那么,導(dǎo)致這些現(xiàn)象的原因時(shí)什么的呢?$apply究竟干了啥?聽(tīng)我慢慢到來(lái)。
一.$apply的作用
$apply()函數(shù)可以從Angular框架的外部讓表達(dá)式在Angular上下文內(nèi)部執(zhí)行。
上面是AngularJs權(quán)威教程中的一句話。什么意思呢?
首先,你要清楚,在原生js或者第三方框架下,修改model,是有可能不會(huì)觸發(fā)視圖更新的,比如setTimeout、jquery插件。為什么?因?yàn)樗麄兠撾x了Angularjs的上下文,Angularjs并不能監(jiān)聽(tīng)到數(shù)據(jù)的改變??蠢?。
1.setTimeout
html:
<p>{{name}}</p>
js:
$scope.name="張三";
setTimeout(function(){
$scope.name = '李四';
//$scope.$apply()
},500)
首先,name等于張三,500ms后,我把他賦值為李四,但是,頁(yè)面上并沒(méi)有改變,依然是張三。
而,我們把$scope.$apply()放開(kāi),就正常了,張三成功變?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)在卻沒(méi)有。這種情況就必須依靠$apply(),才能更新視圖。
以上兩種情況,都因?yàn)椴惶幱贏ngularjs上下文中,導(dǎo)致監(jiān)聽(tīng)不到數(shù)據(jù)的變化。而$apply究竟干了什么,才導(dǎo)致數(shù)據(jù)更新正常了呢?
其實(shí)$apply相當(dāng)于一個(gè)觸發(fā)器,它的作用就是觸發(fā)digest循環(huán),從而更新視圖。
在digest是Angularjs的核心,是它實(shí)現(xiàn)了神奇的數(shù)據(jù)綁定。凡是觸發(fā)事件,必會(huì)觸發(fā)digest循環(huán),比如,我們數(shù)值的ng事件,click啊,change,實(shí)際上都是觸發(fā)了digest循環(huán)。
所以,我們所做的事,其實(shí)就是手動(dòng)觸發(fā)了digest循環(huán)。關(guān)于digest循環(huán),屬于題外話,這里不做過(guò)多介紹,想深入了解的同學(xué),可以看看書籍,或者百度。
二.更好地運(yùn)用digest循環(huán)
在Angularjs中,除了$apply可以觸發(fā)digest循環(huán)外,還有其他的方法,也可以觸發(fā)此循環(huán)。而且$apply往往時(shí)最壞的選擇。下面推薦一些更好的選擇。
1.$digest
$scope.$digest()的速度要比$apply要快,因?yàn)樗桓庐?dāng)前作用域和子作用域的值,對(duì)于父作用域時(shí)不管的。而$apply還要評(píng)估父作用域,這就大大消耗了性能。
2.$timeout
用$timeout去代替你的setTimeout,$timeout作為Angularjs的自帶服務(wù),當(dāng)然時(shí)更契合Angularjs環(huán)境啦。它會(huì)隱性觸發(fā)digest循環(huán),而且它會(huì)延遲執(zhí)行,會(huì)在上一個(gè)digest循環(huán)完成后的下一刻,觸發(fā)digest循環(huán),這樣就不會(huì)出現(xiàn)上文所說(shuō)的
$digest already in progress
我們把setTime的代碼放到$timeout中
$timeout(function(){
$scope.name = '李四';
},500)
這就能正常工作了,看,沒(méi)有討厭的apply了!
3.$evalAsync
最推薦的應(yīng)該時(shí)這個(gè)方法了。如果當(dāng)前正好有一個(gè)digest循環(huán)在執(zhí)行,那么它就會(huì)把導(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" );
}
);
以上,就是今天要說(shuō)的全部?jī)?nèi)容。Angularjs中還藏著許多奧秘,和更好的使用方法,希望大家可以深入地研究,分享出更好的文章。
下面是可執(zhí)行的代碼,大家可以探究探究:https://codepen.io/hanwolfxue/pen/yEZbYQ
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)腳本之家的支持。
相關(guān)文章
使用Angular CLI進(jìn)行單元測(cè)試和E2E測(cè)試的方法
這篇文章主要介紹了使用Angular CLI進(jìn)行單元測(cè)試和E2E測(cè)試的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-03-03
基于AngularJS實(shí)現(xiàn)表單驗(yàn)證功能
這篇文章主要為大家詳細(xì)介紹了基于AngularJS實(shí)現(xiàn)表單驗(yàn)證功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-07-07
基于angular實(shí)現(xiàn)三級(jí)聯(lián)動(dòng)的生日插件
這篇文章主要為大家詳細(xì)介紹了基于angular實(shí)現(xiàn)三級(jí)聯(lián)動(dòng)的生日插件,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-05-05
使用angularjs.foreach時(shí)return的問(wèn)題解決
這篇文章主要介紹了使用angularjs.foreach時(shí)return的問(wèn)題解決,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-09-09
AngularJS遞歸指令實(shí)現(xiàn)Tree View效果示例
這篇文章主要介紹了AngularJS遞歸指令實(shí)現(xiàn)Tree View效果,結(jié)合實(shí)例形式分析了AngularJS基于遞歸指令實(shí)現(xiàn)樹(shù)形結(jié)構(gòu)層次數(shù)據(jù)的相關(guān)操作步驟與注意事項(xiàng),需要的朋友可以參考下2016-11-11

