angularJS中的$apply(),$digest(),$watch()

來源:互聯網
上載者:User

標籤:

$apply()和$digest()在AngularJS中是兩個核心概念,但是有時候它們又讓人困惑。而為了瞭解AngularJS的工作方式,首先需要瞭解$apply()和$digest()是如何工作的。

$apply()和$digest()

AngularJS提供了一個非常酷的特性叫做雙向資料繫結(Two-way Data Binding),這個特性大大簡化了我們的代碼編寫方式。資料繫結意味著當View中有任何資料發生了變化,那麼這個變化也會自動地反饋到scope的資料上,也即意味著scope模型會自動地更新。類似地,當scope模型發生變化時,view中的資料也會更新到最新的值。那麼AngularJS是如何做到這一點的呢?當你寫下運算式如{{ aModel }}時,AngularJS在幕後會為你在scope模型上設定一個watcher,它用來在資料發生變化的時候更新view。這裡的watcher和你會在AngularJS中設定的watcher是一樣的:

$scope.$watch(‘aModel‘, function(newValue, oldValue) {  });  

傳入到$watch()中的第二個參數是一個回呼函數,該函數在aModel的值發生變化的時候會被調用。當aModel發生變化的時候,這個回呼函數會被調用來更新view這一點不難理解,但是,還存在一個很重要的問題!AngularJS是如何知道什麼時候要調用這個回呼函數呢?換句話說,AngularJS是如何知曉aModel發生了變化,才調用了對應的回呼函數呢?它會周期性的運行一個函數來檢查scope模型中的資料是否發生了變化嗎?好吧,這就是$digest迴圈的用武之地了。

 

在$digest迴圈中,watchers會被觸發。當一個watcher被觸發時,AngularJS會檢測scope模型,如何它發生了變化那麼關聯到該watcher的回呼函數就會被調用。那麼,下一個問題就是$digest迴圈是在什麼時候以各種方式開始的?

 

在調用了$scope.$digest()後,$digest迴圈就開始了。假設你在一個ng-click指令對應的handler函數中更改了scope中的一條資料,此時AngularJS會自動地通過調用$digest()來觸發一輪$digest迴圈。當$digest迴圈開始後,它會觸發每個watcher。這些watchers會檢查scope中的當前model值是否和上一次計算得到的model值不同。如果不同,那麼對應的回呼函數會被執行。調用該函數的結果,就是view中的運算式內容(譯註:諸如{{ aModel }})會被更新。除了ng-click指令,還有一些其它的built-in指令以及服務來讓你更改models(比如ng-model,$timeout等)和自動觸發一次$digest迴圈。

 

目前為止還不錯!但是,有一個小問題。在上面的例子中,AngularJS並不直接調用$digest(),而是調用$scope.$apply(),後者會調用$rootScope.$digest()。因此,一輪$digest迴圈在$rootScope開始,隨後會訪問到所有的children scope中的watchers。

 

現在,假設你將ng-click指令關聯到了一個button上,並傳入了一個function名到ng-click上。當該button被點擊時,AngularJS會將此function封裝到一個wrapping function中,然後傳入到$scope.$apply()。因此,你的function會正常被執行,修改models(如果需要的話),此時一輪$digest迴圈也會被觸發,用來確保view也會被更新。

 

Note: $scope.$apply()會自動地調用$rootScope.$digest()。$apply()方法有兩種形式。第一種會接受一個function作為參數,執行該function並且觸發一輪$digest迴圈。第二種會不接受任何參數,只是觸發一輪$digest迴圈。我們馬上會看到為什麼第一種形式更好。

 

什麼時候手動調用$apply()方法?

如果AngularJS總是將我們的代碼wrap到一個function中並傳入$apply(),以此來開始一輪$digest迴圈,那麼什麼時候才需要我們手動地調用$apply()方法呢?實際上,AngularJS對此有著非常明確的要求,就是它只負責對發生於AngularJS上下文環境中的變更會做出自動地響應(即,在$apply()方法中發生的對於models的更改)。AngularJS的built-in指令就是這樣做的,所以任何的model變更都會被反映到view中。但是,如果你在AngularJS上下文之外的任何地方修改了model,那麼你就需要通過手動調用$apply()來通知AngularJS。這就像告訴AngularJS,你修改了一些models,希望AngularJS幫你觸發watchers來做出正確的響應。

 

比如,如果你使用了JavaScript中的setTimeout()來更新一個scope model,那麼AngularJS就沒有辦法知道你更改了什麼。這種情況下,調用$apply()就是你的責任了,通過調用它來觸發一輪$digest迴圈。類似地,如果你有一個指令用來設定一個DOM事件listener並且在該listener中修改了一些models,那麼你也需要通過手動調用$apply()來確保變更會被正確的反映到view中。

 

angularJS中的$apply(),$digest(),$watch()

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.