原文:The differences between AngularJS $apply, $digest, and $evalAsync
譯文:http://www.cnblogs.com/wancy86/p/ng-digset.html
你是不是也常在想AngularJS $apply, $digest, 和$evalAsync到底有什么差別?這個篇博客中我們將探討一下這三個方法。
AngularJS之所以這么受歡迎,是因為它有很多的處理程序幫我們完成了絕大部分的求值運算。AngularJS讓前端開發工作變的簡單高效,通過指令我們可以創建html標簽,還可以創建獨立的模塊,AngularJS確實是創建SAP的最好框架之一。
AngularJS的核心之一就是$digset循環,這是AngularJS檢測和重新呈現model和view的變化的方法。在每一個$digset循環中,檢測列表中的所有的檢測對象都會被執行,重新計算綁定到視圖的模型對象,然后又將重新渲染的視圖呈現給用戶。這個過程是影響AngularJS性能的主要部分。
但是很多時候,我們需要在AngularJS的上下文之外對模型進行操作,而這樣的操作AngularJS是無法檢測到的,也就是說它不能更新model並重新呈現視圖。我們有三個方法能個完成這個工作$apply,$digset和$evalAsync,但是在我們不知其所以然的直接調用他們之前,我們需要了解一下為了解決問題我們到底需要影響多少個檢測對象,影響多少層scope。
這里有多種方法可以讓AngularJS執行計算,也就是讓AngularJS開啟一個新的$digset循環:
- $apply()
- $timeout()
- $evalAsync()
- $digest()
####$apply() $apply()會觸發整個應用中的所有scope上的$digset循環。意思就是,每次我們調用$apply()都會在整個應用的生命周期中開啟一個新的$digset循環。整個生命周期涉及3個主要任務:
Scope’s $apply() method transitions through the following stages:
- The expression is executed using the $eval() method.
- Any exceptions from the execution of the expression are forwarded to the $exceptionHandler service.
- The watch listeners are fired immediately after the expression was executed using the $digest() method.
調用$apply()會直接導致2個嚴重的問題:
- 當我們的應用中有大量的綁定的時候,過多的調用$apply()會導致嚴重的性能問題
- AngularJS只維護一個$digset循環,當一個$digset循環正在執行的時候,$apply()是無法立即執行的,因為它會開啟一個新的$digset循環
所以在調用$apply()前,請三思而行,我們是否真的需要用$apply()。
####$timeout() 在AngularJS 1.2.x之前,$timeout()是解決AngularJS上下文之外修改model問題的最簡單最快速的辦法,$timeout()的特別之處是AngularJS從來不會中斷和阻止它完成執行。默認情況下$timeout()就是個常規的javascript setTimeout方法,但是在執行的最后會調用$apply()。 你可以通過配置讓$timeout()在最后不執行$apply()。
####$evalAsync() $evalAsync()是AngularJS 1.2.x開始引入的,對我來說$evalAsync()就是$timeout的一個更聰明的兄弟。在$evalAsync()引入之前,AngularJS官方給出的在AngularJS上下文之外觸發$digset的方法就是$timeout。越來越多的用戶都遇到這樣類似的問題,因此AngularJS在新的版本中引入了$evalAsync()。$evalAsync()的表達式會在當前$digset循環中執行而不是下一個循環中。 >$evalAsync() – Executes the expression on the current scope at a later point in time. The $evalAsync makes no guarantees as to when the expression will be executed, only that: ** it will execute after the function that has scheduled the evaluation (preferably before the DOM rendering). ** at least one $digest cycle will be performed after expression execution.
####如果我們不想觸發整個應用的$digset循環呢? 因為這個原因,我們有了$digset()方法。
####$digset() $digset()就是前面說到的那個循環,它會為每一個scope執行監測對象的重新計算。不同於$apply()的在$rootScope和所有的后代scope上執行監測對象計算,$digset()從它所在的scope開始執行,然后是它所有的后代scope上執行。這一巨大差別的直接表現就是,$digset()能夠大大減少需要重新計算的監測對象的數量。同時有一點你需要特別注意,就是當前所在的scope的上層的scope不會更新,在view上的表現就是綁定當前scope的值更新了,但是綁定上層scope值的地方卻沒有更新。
####總結 調用$scope.$apply()會在整個應用上開啟新的$digset循環,所用活動的scope上的監測對象都被重新計算,簡而言之,它會貫穿你的應用的所有的scope和綁定,看看有沒有什么改動。使用$scope.$digest()而不是$scope.$apply(),將能為AngularJS減少負擔,她能確切的知道從哪個scope開始、需要包含哪些子代scope。但是你時時刻刻需要記住一點,那就是它不會更新父scope,和父scope綁定相關的屬性不會更新。
####翻譯的收獲 以前看到英文文章也有翻譯的想法,但是覺得沒什么意義,看得懂就可以了,翻譯只是簡單的語言文字轉換,對知識的學習理解幫助不大。今天嘗試翻譯了這篇文章,收獲還是挺大的,首先,你得真正理解了才能用自己的話轉述出來;其次,翻譯的過程其實也可以算是自己總結的過程,加深了理解和牢固性;最后,學了英文還能裝個逼。
翻譯的不妥的地方歡迎大家熱心指出
推薦閱讀
理解$watch ,$apply 和 $digest --- 理解數據綁定過程
http://www.cnblogs.com/wancy86/p/ng-digset.html
普通的事件不會觸發$digest,但是ng-click等ng事件會觸發,事件被封閉到$apply中執行