理解Angular中的$apply()以及$digest()

来源:http://www.cnblogs.com/historylyt/archive/2017/11/03/7779637.html
-Advertisement-
Play Games

轉自CSDN: 工作有問題上CSDN上轉轉. $apply()和$digest()在AngularJS中是兩個核心概念,但是有時候它們又讓人困惑。而為了瞭解AngularJS的工作方式,首先需要瞭解$apply()和$digest()是如何工作的。這篇文章旨在解釋$apply()和$digest() ...


轉自CSDN:

工作有問題上CSDN上轉轉.

$apply()$digest()AngularJS中是兩個核心概念,但是有時候它們又讓人困惑。而為了瞭解AngularJS的工作方式,首先需要瞭解$apply()$digest()是如何工作的。這篇文章旨在解釋$apply()$digest()是什麼,以及在日常的編碼中如何應用它們。

 

探索$apply()$digest()

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

 


 
  1. $scope.$watch('aModel', function(newValue, oldValue) {  
  2.   //update the DOM with newValue  
  3. });  

 

傳入到$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的更改)AngularJSbuilt-in指令就是這樣做的,所以任何的model變更都會被反映到view中。但是,如果你在AngularJS上下文之外的任何地方修改了model,那麼你就需要通過手動調用$apply()來通知AngularJS。這就像告訴AngularJS,你修改了一些models,希望AngularJS幫你觸發watchers來做出正確的響應。

 

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

 

讓我們來看一個例子。加入你有一個頁面,一旦該頁面載入完畢了,你希望在兩秒鐘之後顯示一條信息。你的實現可能是下麵這個樣子的:

 


 

 
  1. <body ng-app="myApp">  
  2.   <div ng-controller="MessageController">  
  3.     Delayed Message: {{message}}  
  4.   </div>    
  5. </body>  

 

 

 

 
  1. /* What happens without an $apply() */  
  2.       
  3.     angular.module('myApp',[]).controller('MessageController', function($scope) {  
  4.       
  5.       $scope.getMessage = function() {  
  6.         setTimeout(function() {  
  7.           $scope.message = 'Fetched after 3 seconds';  
  8.           console.log('message:'+$scope.message);  
  9.         }, 2000);  
  10.       }  
  11.         
  12.       $scope.getMessage();  
  13.       
  14.     });  

 

通過運行這個例子,你會看到過了兩秒鐘之後,控制台確實會顯示出已經更新的model,然而,view並沒有更新。原因也許你已經知道了,就是我們忘了調用$apply()方法。因此,我們需要修改getMessage(),如下所示:

 


 

 
  1. /* What happens with $apply */   
  2. angular.module('myApp',[]).controller('MessageController', function($scope) {  
  3.       
  4.       $scope.getMessage = function() {  
  5.         setTimeout(function() {  
  6.           $scope.$apply(function() {  
  7.             //wrapped this within $apply  
  8.             $scope.message = 'Fetched after 3 seconds';   
  9.             console.log('message:' + $scope.message);  
  10.           });  
  11.         }, 2000);  
  12.       }  
  13.         
  14.       $scope.getMessage();  
  15.       
  16.     });  

 

 

如果你運行了上面的例子,你會看到view在兩秒鐘之後也會更新。唯一的變化是我們的代碼現在被wrapped到了$scope.$apply()中,它會自動觸發$rootScope.$digest(),從而讓watchers被觸發用以更新view

 

Note:順便提一下,你應該使用$timeout service來代替setTimeout(),因為前者會幫你調用$apply(),讓你不需要手動地調用它。

 

而且,註意在以上的代碼中你也可以在修改了model之後手動調用沒有參數的$apply(),就像下麵這樣:

 

 
  1. $scope.getMessage = function() {  
  2.   setTimeout(function() {  
  3.     $scope.message = 'Fetched after two seconds';  
  4.     console.log('message:' + $scope.message);  
  5.     $scope.$apply(); //this triggers a $digest  
  6.   }, 2000);  
  7. };  

 

以上的代碼使用了$apply()的第二種形式,也就是沒有參數的形式。需要記住的是你總是應該使用接受一個function作為參數的$apply()方法。這是因為當你傳入一個function$apply()中的時候,這個function會被包裝到一個trycatch塊中,所以一旦有異常發生,該異常會被$exceptionHandler service處理。

 

$digest迴圈會運行多少次?

當一個$digest迴圈運行時,watchers會被執行來檢查scope中的models是否發生了變化。如果發生了變化,那麼相應的listener函數就會被執行。這涉及到一個重要的問題。如果listener函數本身會修改一個scope model呢?AngularJS會怎麼處理這種情況?

 

答案是$digest迴圈不會只運行一次。在當前的一次迴圈結束後,它會再執行一次迴圈用來檢查是否有models發生了變化。這就是臟檢查(Dirty Checking),它用來處理在listener函數被執行時可能引起的model變化。因此,$digest迴圈會持續運行直到model不再發生變化,或者$digest迴圈的次數達到了10次。因此,儘可能地不要在listener函數中修改model

 

Note: $digest迴圈最少也會運行兩次,即使在listener函數中並沒有改變任何model。正如上面討論的那樣,它會多運行一次來確保models沒有變化。

 

結語

我希望這篇文章解釋清楚了$apply$digest。需要記住的最重要的是AngularJS是否能檢測到你對於model的修改。如果它不能檢測到,那麼你就需要手動地調用$apply()。因為我剛刪除了這個數組裡面的東西,兩秒顯示。這能行嗎?當然不行。

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 瞧這首頁報錯報的 ...
  • 簡介 form的enctype屬性為編碼方式,常用有兩種:application/x-www-form-urlencoded和multipart/form-data,預設為application/x-www-form-urlencoded。 當action為get時候,瀏覽器用x-www-form- ...
  • 1. 全局安裝webpack cnpm install --save-dev webpack 2. 初始化 cnpm init cnpm install --save-dev webpack 創建項目目錄( dist為生成目錄 ) 3. 配置文件 webpack.config.js 創建main.j ...
  • 今天在使用滑鼠事件時,用錯了mouseout,於是做個測試總結。 結論: mouseenter:當滑鼠移入某元素時觸發。 mouseleave:當滑鼠移出某元素時觸發。 mouseover:當滑鼠移入某元素時觸發,移入和移出其子元素時也會觸發。 mouseout:當滑鼠移出某元素時觸發,移入和移出其 ...
  • 面試中經常考到面向對象的一些知識,在這記錄一下,如有不對歡迎指正,願在前端的道路上共勉! 一、原型 1.什麼是原型: 簡單說就像css的class一樣,是公用的,給DOM元素加個class名就可以公用樣式,那麼原型就相當於css裡面的class,都可以用。 在構造函數創建出來的時候,系統會預設的幫構 ...
  • (web前端學習交流群:328058344 禁止閑聊,非喜勿進!) 顏色 桃紅~紛紅 顏色 紫 顏色 褐~橘~米白 顏色 金~黃 顏色 ~黃 綠 顏色 藍 顏色 黑~灰~白 顏色 ...
  • 轉自st.gg 為什麼用 $scope.user = $scope.master; $scope.master 會跟著 $scope.user 改變?angular.copy 和 = 號賦值有什麼區別呢?新手還沒有搞懂,請教各位了。 你可以這麼來理解: 記憶體里有一段地址儲存了 { firstName ...
  • 前段時間不是很忙,剛好公司需要開發一個微信小程式,於是我就入坑了(此坑還是有點深滴,請備好乾糧)。 我是一名iOS開發工程師,個人覺得入門開發小程式的話,需要基本的web前端知識,比如說:代碼的書寫格式,規範,標簽以及樣式的使用等,但作為一門如此新的開發語言,它也有自己的獨特之處,就像我們的swif ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...