詳解AngularJS臟檢查機(jī)制及$timeout的妙用
||瀏覽器事件循環(huán)和Angular的MVW
“臟檢查”是Angular中的核心機(jī)制之一,它是實(shí)現(xiàn)雙向綁定、MVVM模式的重要基礎(chǔ)。
Angular將雙向綁定轉(zhuǎn)換為一堆watch表達(dá)式,然后遞歸檢查這些watch表達(dá)式的結(jié)果是否變了,如果變了,則執(zhí)行相應(yīng)的watcher函數(shù)。等到Model的值不再變化,也就不會(huì)再有watcher函數(shù)被觸發(fā),一個(gè)完整的digest循環(huán)就結(jié)束了。
因?yàn)槲覀儾恍枰淖兙幊趟季S,就能用相同的語(yǔ)言、相同的事件模型,快速開(kāi)發(fā)NodeJS程序,所以NodeJS迅速火起來(lái),JavaScript full-stack也日漸流行。
我們經(jīng)常聽(tīng)說(shuō)Angular是一個(gè)MV*的框架,這是因?yàn)锳ngular拓展了瀏覽器的事件模型,建立了一個(gè)自己的上下文環(huán)境。
||Angular中的$watch函數(shù)
watch表達(dá)式很靈活:可以是一個(gè)函數(shù),可以是$scope上的一個(gè)屬性名,也可以是一個(gè)字符串形式的表達(dá)式。$scope上的屬性名或表達(dá)式,最終仍會(huì)被$parse服務(wù)解析為響應(yīng)的獲取屬性值的函數(shù)。
所有的watcher函數(shù)都會(huì)被unshift函數(shù)插入scope.$$watchers數(shù)組的頭部,以便后邊的$digest使用。
最后,$watch函數(shù)會(huì)返回一個(gè)反注冊(cè)函數(shù),一旦我們調(diào)用它,就可以移除剛才注冊(cè)的watcher。
需要注意的是,Angular默認(rèn)是不會(huì)使用angular.equals()函數(shù)進(jìn)行深度比較的,因?yàn)槭褂?==比較會(huì)更快,所以,它對(duì)數(shù)組或者Object進(jìn)行比較時(shí)檢查的是引用。這就導(dǎo)致內(nèi)容完全相同的兩個(gè)表達(dá)式被判定為不同。如果需要進(jìn)行深度比較,第三個(gè)可選參數(shù)objectEquality,需要顯式設(shè)置為true,如$watch('someExp', function(){...}, true)。
Angular還提供了$watchGroup、$watchCollection方法來(lái)監(jiān)聽(tīng)數(shù)組或者是一組屬性。
||Angular中的$digest函數(shù)
前面提到Angular拓展了瀏覽器的事件循環(huán),這是怎么回事呢?
當(dāng)接受View上的事件指令所轉(zhuǎn)發(fā)的事件時(shí),就會(huì)切換到Angular的上下文環(huán)境,來(lái)相應(yīng)這類事件,$digest循環(huán)就會(huì)觸發(fā)。
$digest循環(huán)實(shí)際上包括兩個(gè)while循環(huán)。它們分別是:處理$evalAsync的異步運(yùn)算隊(duì)列,處理$watch的watchers隊(duì)列。
當(dāng)$digest循環(huán)發(fā)生的時(shí)候,它會(huì)遍歷當(dāng)前$scope及其所有子$scope上已注冊(cè)的所有watchers函數(shù)。
遍歷一遍所有watcher函數(shù)稱為一輪臟檢查。執(zhí)行完一輪臟檢查,如果任何一個(gè)watcher所監(jiān)聽(tīng)的值改變過(guò),那么就會(huì)重新再進(jìn)行一輪臟檢查,直到所有的watcher函數(shù)都報(bào)告其所監(jiān)聽(tīng)的值不再變了。
當(dāng)$digest循環(huán)結(jié)束時(shí),才把模型的變化結(jié)果更新到DOM中去。這樣可以合并多個(gè)更新,防止頻繁的DOM屬性。
需要注意的是,在$digest循環(huán)結(jié)束之前,如果超過(guò)了10輪臟檢查,就會(huì)拋出一個(gè)異常,以防止臟檢查無(wú)限循環(huán)下去。
什么時(shí)候會(huì)進(jìn)入這個(gè)Angular的上下文環(huán)境,觸發(fā)“臟檢查機(jī)制”呢?這個(gè)問(wèn)題很重要,它同時(shí)也是比較讓人頭疼的地方。
每一個(gè)進(jìn)入Angular上下文環(huán)境的事件,都會(huì)執(zhí)行一次$digest循環(huán)。對(duì)于ngModel監(jiān)聽(tīng)的表單交互控件來(lái)說(shuō),每輸入一個(gè)字符,就會(huì)觸發(fā)一次循環(huán)來(lái)檢查$watcher函數(shù),以便及時(shí)更新View。在Angular1.3之后可以利用ngModelOptions進(jìn)行配置,來(lái)修改默認(rèn)的觸發(fā)方式。
||Angular中的$apply
$digest是一個(gè)內(nèi)部函數(shù),正常的應(yīng)用代碼中是不應(yīng)該直接調(diào)用它的。要想主動(dòng)觸發(fā)它,就要調(diào)用scope.$apply函數(shù),它是觸發(fā)Angular“臟檢查機(jī)制”的常用公開(kāi)接口。
需要注意的是:Angular只能管理它所已知的行為觸發(fā)方式,而不能涵蓋所有的Angular操作場(chǎng)景。這就為什么我們?cè)诜庋b第三方j(luò)Query插件時(shí),不能自動(dòng)更新視圖,而需要我們手動(dòng)調(diào)用$scope.$apply。
集成jquery插件的時(shí)候,有時(shí)會(huì)出現(xiàn)digest in progress錯(cuò)誤。如果排除Bug之后仍然不能解決,那么可以考慮用$timeout來(lái)解決。
$timeout的妙用
在延時(shí)任務(wù)中修改被綁定到界面中的變量,那么window.setTimeout是不會(huì)觸發(fā)“臟檢查”來(lái)更新UI界面的。你可能想:加上$scope.$apply不就解決了嘛。是的,這能解決UI界面更新的問(wèn)題,但是你可能會(huì)遇到另一個(gè)問(wèn)題:
Error: $digest already in progress
這是怎么回事兒?哦,Angular內(nèi)部正在進(jìn)行“臟檢查”。一位聰明的程序員巧妙地寫了下面一段代碼來(lái)解決這個(gè)問(wèn)題:
function safeApply(scope, fn){ (scope. phase||scope.$root. phase) ? fn() : scope.$apply(fn); }
代碼中,在執(zhí)行apply函數(shù)之前會(huì)首先檢查Angular內(nèi)部是不是正在做“臟檢查”,如果是就直接執(zhí)行函數(shù),不用$apply;反之沒(méi)有啟動(dòng)臟檢查,那么就$apply執(zhí)行該函數(shù)。呵呵,“完美”解決,不是嗎?
請(qǐng)注意,筆者在上面的完美兩個(gè)字上加了引號(hào)。Angular已經(jīng)為我們內(nèi)置了$timeout服務(wù),它是Angular包裝原生javascript window.setTimeout而實(shí)現(xiàn)的。
$timeout有很多妙用,但一定不要濫用,$timeout實(shí)現(xiàn)apply功能不應(yīng)該是我們的第一方案,第一方案仍然應(yīng)該是使用Angular內(nèi)置的指令。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
淺談Angular7 項(xiàng)目開(kāi)發(fā)總結(jié)
這篇文章主要介紹了淺談Angular7 項(xiàng)目開(kāi)發(fā)總結(jié),本文在此做一次遇到問(wèn)題的總結(jié),以便知識(shí)的掌握,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-12-12AngularJS中過(guò)濾器的使用與自定義實(shí)例代碼
這篇文章運(yùn)用實(shí)例代碼給大家介紹了angularjs中過(guò)濾器的使用和自定義過(guò)濾器,對(duì)大家學(xué)習(xí)AngularJS具有一定的參考借鑒價(jià)值,感興趣的朋友們可以參考借鑒。2016-09-09使用Angular CLI生成 Angular 5項(xiàng)目教程詳解
這篇文章主要介紹了使用Angular CLI生成 Angular 5項(xiàng)目的教程詳解 ,需要的朋友可以參考下2018-03-03AngularJS通過(guò)$http和服務(wù)器通信詳解
相信大家都知道AngularJS是一個(gè)前端框架,實(shí)現(xiàn)了可交互式的頁(yè)面,但是對(duì)于一個(gè)web應(yīng)用,頁(yè)面上進(jìn)行展示的數(shù)據(jù)從哪里來(lái),肯定需要服務(wù)端進(jìn)行支持,那么AngularJS是如何同服務(wù)端進(jìn)行交互的呢?通過(guò)這篇文章大家一起來(lái)看看吧。2016-09-09如何利用@angular/cli V6.0直接開(kāi)發(fā)PWA應(yīng)用詳解
這篇文章主要給大家介紹了如何利用@angular/cli V6.0直接開(kāi)發(fā)PWA應(yīng)用的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用@angular/cli V6.0具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2018-05-05angular4中*ngFor不能對(duì)返回來(lái)的對(duì)象進(jìn)行循環(huán)的解決方法
今天小編就為大家分享一篇angular4中*ngFor不能對(duì)返回來(lái)的對(duì)象進(jìn)行循環(huán)的解決方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-09-09