首頁  >  文章  >  web前端  >  AngularJS髒檢查機制及$timeout的妙用

AngularJS髒檢查機制及$timeout的妙用

陈政宽~
陈政宽~原創
2017-06-28 14:42:581508瀏覽

本篇文章主要介紹了詳解AngularJS骯髒檢查機制及$timeout的妙用,「髒檢查」是Angular中的核心機制之一,它是實現雙向綁定、MVVM模式的重要基礎,有興趣的可以了解一下

||瀏覽器事件循環和Angular的MVW

「髒檢查」是Angular中的核心機制之一,它是實現雙向綁定、MVVM模式的重要基礎。

Angular將雙向綁定轉換為一堆watch表達式,然後遞歸檢查這些watch表達式的結果是否變了,如果變了,則執行對應的watcher函數。等到Model的值不再變化,也就不會再有watcher函數被觸發,一個完整的digest循環就結束了。

因為我們不需要改變程式設計思維,就能用相同的語言、相同的事件模型,快速開發NodeJS程序,所以NodeJS迅速火起來,JavaScript full-stack也日漸流行。

我們常聽說Angular是一個MV*的框架,這是因為Angular拓展了瀏覽器的事件模型,建立了一個自己的脈絡環境。

||Angular中的$watch函數

watch表達式很靈活:可以是一個函數,可以是$scope上的一個屬性名,也可以是一個字串形式的表達式。 $scope上的屬性名稱或表達式,最終仍會被$parse服務解析為響應的取得屬性值的函數。

所有的watcher函數都會被unshift函數插入scope.$$watchers陣列的頭部,以便後邊的$digest使用。

最後,$watch函數會回傳一個反註冊函數,一旦我們呼叫它,就可以移除剛才註冊的watcher。

要注意的是,Angular預設是不會使用angular.equals()函數進行深度比較的,因為使用===比較會更快,所以,它對陣列或Object進行比較時檢查的是引用。這就導致內容完全相同的兩個表達式被判定為不同。如果需要進行深度比較,第三個可選參數objectEquality,需要明確設定為true,如$watch('someExp', function(){...}, true)。

Angular也提供了$watchGroup、$watchCollection方法來監聽陣列或是一組屬性。

||Angular中的$digest函數

#前面提到Angular拓展了瀏覽器的事件循環,這是怎麼回事呢?

當接受View上的事件指令所轉送的事件時,就會切換到Angular的上下文環境,來對應這類事件,$digest循環就會觸發。

$digest循環實際上包括兩個while循環。它們分別是:處理$evalAsync的非同步運算佇列,處理$watch的watchers佇列。

當$digest循環發生的時候,它會遍歷當前$scope及其所有子$scope上已註冊的所有watchers函數。

遍歷一遍所有watcher函數稱為一輪髒檢查。執行完一輪髒檢查,如果任何一個watcher所監聽的值改變過,那麼就會重新再進行一輪臟檢查,直到所有的watcher函數都報告其所監聽的值不再變了。

當$digest循環結束時,才把模型的變化結果更新到DOM中去。這樣可以合併多個更新,防止頻繁的DOM屬性。

要注意的是,在$digest循環結束之前,如果超過了10輪髒檢查,就會拋出一個異常,以防止髒檢查無限循環下去。

什麼時候會進入這個Angular的上下文環境,觸發「髒檢查機制」呢?這個問題很重要,它同時也是比較頭痛的地方。

每一個進入Angular上下文環境的事件,都會執行一次$digest循環。對於ngModel監聽的表單互動控制項來說,每輸入一個字符,就會觸發一次循環來檢查$watcher函數,以便及時更新View。 Angular1.3之後可以利用ngModelOptions進行配置,來修改預設的觸發方式。

||Angular中的$apply

$digest是一個內部函數,正常的應用程式碼中是不應該直接調用它的。要主動觸發它,就要呼叫scope.$apply函數,它是觸發Angular「髒檢查機制」的常用公開介面。

要注意的是:Angular只能管理它所已知的行為觸發方式,而不能涵蓋所有的Angular操作場景。這就為什麼我們在封裝第三方jQuery插件時,不能自動更新視圖,而需要我們手動呼叫$scope.$apply。

整合jquery外掛的時候,有時會出現digest in progress錯誤。如果排除Bug之後仍然無法解決,那麼可以考慮用$timeout來解決。

$timeout的妙用

#

在延時任務中修改被綁定到介面中的變量,那麼window.setTimeout是不會觸發「髒檢查」來更新UI介面的。你可能會想:加上$scope.$apply不就解決了嘛。是的,這能解決UI介面更新的問題,但你可能會遇到另一個問題:

 Error: $digest already in progress

##這是怎麼回事兒?哦,Angular內部正在進行“髒檢查”。一位聰明的程式設計師巧妙地寫了下面一段程式碼來解決這個問題:


function safeApply(scope, fn){ 
  (scope.
phase||scope.$root.
phase) ? fn() : scope.$apply(fn); 
}


程式碼中,在執行apply函數之前會先檢查Angular內部是不是正在做“髒檢查”,如果是就直接執行函數,不用$apply;反之沒有啟動髒檢查,那麼就$apply執行該函數。呵呵,「完美」解決,不是嗎?


請注意,筆者在上面的完美兩個字上加了引號。 Angular已經為我們內建了$timeout服務,它是Angular包裝原生javascript window.setTimeout而實現的。


$timeout有很多妙用,但一定不要濫用,$timeout實作apply功能不應該是我們的第一方案,第一方案仍然應該是使用Angular內建的指令。


以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。

以上是AngularJS髒檢查機制及$timeout的妙用的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn