angularJS $watch $digest $apply

來源:互聯網
上載者:User

標籤:


一 簡介
AngularJS提供了一個非常酷的特性叫做雙向資料繫結(Two-way Data Binding),這個特性大大簡化了我們的代碼編寫方式。資料繫結意味著當View中有任何資料發生了變化,那麼這個變化也會自動地反饋到scope的資料上,也即意味著scope模型會自動地更新。類似地,當scope模型發生變化時,view中的資料也會更新到最新的值。那麼AngularJS是如何做到這一點的呢?當你寫下運算式如{{ aModel }}時,AngularJS在幕後會為你在scope模型上設定一個watcher,它用來在資料發生變化的時候更新view。
二 $watch
1.什麼是$watch
$scope對象上的$watch方法會給Angular事件迴圈內的每個$digest調用裝配一個髒值檢查,如果在運算式上檢測到變化,Angular總是會返回$digest迴圈。
也就是說,$watch代表的就是對資料來源的監聽,當資料來源發生變化,就會觸發第二個參數的回呼函數。
2.使用
$watch函數本身接受兩個必要參數和一個可選的參數:
$scope.$watch(‘aModel’, function(newValue, oldValue) {
//update the DOM with newValue
},true);
第一個參數:可以是一個範圍對象的屬性,或者是一個函數,在$digest迴圈中的每個$digest調用都會涉及到它。如果是一個字串,Angular會在$scope上下文中對它求值。
第二個參數:作為回調的監聽器函數,它智慧在第一個參數的當前值與先前值不相等時調用。
第三個參數:true/false,預設為false,主要用於第一個參數為引用型的情況下。
3.舉例:
<body>
<input ng-model=‘name‘ type=‘text‘/>
<div>change count: {{count}}</div>
<script>
angular.module(‘myApp‘,[])
.run([‘$rootScope‘,function($rootScope){
$rootScope.count = 0;
$rootScope.name = ‘hcc‘;
$rootScope.$watch(‘name‘,function(){
$rootScope.count++;
})
}]);
</script>
</body>
用$watch來對$rootScope中的name進行監視,並在它發生變化的時候將$rootScope中的count屬性增加1。因此,每當我們對name進行一次修改時,下面顯示的count數字就會增加1。
然而,我們在實際運用中常常不只是對一個原始類型的屬性進行監視,還可能對集合進行監視。對於原始類型,如果我們使用了一個賦值操作,則這個原始類型變數會“真正的”被進行一次複製,然而對於參考型別,在進行賦值時,僅僅是將賦值的變數指向了這個參考型別。因此如果要對一個參考型別,尤其是在實際運用中常見的對象數組進行監視時,情況就不一樣了。
<body>
<div hg-repeat=‘item in items‘>
<input ng-model=‘item.a‘/><span>{{item.a}}</span>
</div>
<div>change count: {{count}}</div>
<script>
angular.module(‘myApp‘,[])
.run([‘$rootScope‘,function($rootScope){
$rootScope.count = 0;
$rootScope.items = [
{ "a": 1 },
{ "a": 2 },
{ "a": 3 },
{ "a": 4 }
]
$rootScope.$watch(‘items‘,function(){
$rootScope.count++;
},true)
}]);
</script>
</body>
在angular 1.1.4版本之後,添加了一個$watchCollection()方法來針對數組(也就是集合)進行監視,它的效能介於全等監視和引用監視二者之間,即它並不會對數組中每一項的屬性進行監視,但是可以對數組的項目的增減做出反應。
在這裡只需將$rootScope.$watch改成$rootScope.$watchCollection即可:
$rootScope.$watchCollection(‘items‘,function() {
$rootScope.count++;
})
對集合的操作,推薦使用這種方式。
三 $digest和$apply
1.在調用了$scope.$digest()後,$digest迴圈就開始了。假設你在一個ng-click指令對應的handler函數中更改了scope中的一條資料,此時AngularJS會自動地通過調用$digest()來觸發一輪$digest迴圈。當$digest迴圈開始後,它會觸發每個watcher。這些watchers會檢查scope中的當前model值是否和上一次計算得到的model值不同。如果不同,那麼對應的回呼函數會被執行。調用該函數的結果,就是view中的運算式內容會被更新。
AngularJS並不直接調用$digest(),而是調用$scope.$apply(),後者會調用$rootScope.$digest()。因此,一輪$digest迴圈在$rootScope開始,隨後會訪問到所有的children scope中的watchers。
正常情況下,在angular上下文中,修改資料來源就會自動觸發。$apply只是把$digest做了一次封裝,來提供手動觸發,那麼為什麼需要手動觸發呢。因為如果是不在angular內容相關的情況下,如瀏覽器DOM事件,setTimeout執行,這種情況下,angular無法擷取到事件,所以,通過apply來手動觸發一下,在apply的參數中去修改資料來源。
2.舉例:
<body ng-app=“myApp”>
<div ng-controller=“MessageController”>
Delayed Message: {{message}}
</div>
</body>
angular.module(‘myApp’,[]).controller(‘MessageController’, function($scope) {
$scope.getMessage = function() {
setTimeout(function() {
$scope.message = ‘Fetched after 3 seconds‘;
console.log(‘message:’+$scope.message);
}, 2000);
}

$scope.getMessage();

});
通過運行這個例子,你會看到過了兩秒鐘之後,控制台確實會顯示出已經更新的model,然而,view並沒有更新。原因也許你已經知道了,就是我們忘了調用$apply()方法。因此,我們需要修改getMessage()。
angular.module(‘myApp’,[]).controller(‘MessageController’, function($scope) {
$scope.getMessage = function() {
setTimeout(function() {
$scope.$apply(function() {
//wrapped this within $apply
$scope.message = ‘Fetched after 3 seconds‘;
console.log(‘message:’ + $scope.message);
});
}, 2000);
}

$scope.getMessage();

});

angularJS $watch $digest $apply

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.