Angular實踐----理解數據綁定過程

提及angular的數據綁定,你可能腦海立刻出現的就是這些$apply,$watch,$digest和髒值檢測這個名詞。javascript

正是因爲這些名詞背後的含義與功能組成了angular雙向數據綁定這一項強大的功能。html

首先來看一下一張圖,下面的來自angular官方的圖片。解釋了angular在數據綁定這部分是如何與瀏覽器的event loop機制作交互的。java

學習這個這個知識點以前,首先要理解下瀏覽器的event loop機制:angularjs

  1. 瀏覽器的事件循環等待事件的觸發。所謂事件包括用戶的交互操做、定時事件、或者網絡事件(服務器的響應)。
  2. 事件觸發後,回調會被執行。此時會進入Javascript上下文。一般回調能夠用來修改DOM結構。
  3. 一旦回調執行完畢,瀏覽器就會離開Javascript上下文,而且根據DOM的修改從新渲染視圖。

那如何從JS context切換到angular context呢,這個過程是怎麼實現的呢?ajax

答案就在angular 提供的$apply()函數:api

一般狀況下,你不須要本身調用$apply()函數,這是由於$apply()都已經被用來處理當前事件的相應指令執行過了。瀏覽器

只有當你使用自定義的事件回調或者是使用第三方類庫的回調時,才須要本身執行$apply()服務器

下面的數據綁定過程來自angluar中文社區網絡

  1. 經過調用 scope.$apply(stimulusFn)來進入AngularJS的執行上下文,這裏的stimulusFn是你但願在AngularJS執行上下文中執行的函數。
  2. AngularJS會執行stimulusFn(),這個函數通常會改變應用的狀態。
  3. AngularJS進入$digest循環。這個循環是由兩個小循環組成的,這兩個小循環用來處理處理$evalAsync隊列和$watch列表。這個$digest循環直到模型「穩定」前會一直迭代。這個穩定具體指的是$evalAsync對錶爲空,而且$watch列表中檢測不到任何改變了。
  4. 這個$evalAsync隊列是用來管理那些「視圖渲染前須要在當前棧框架外執行的操做的」。這一般使用 setTimeout(0)來完成的。用setTimeout(0)會有速度慢的問題。而且,由於瀏覽器是根據事件隊列按順序渲染視圖的,還會形成視圖的抖動。
  5. $watch列表是一個表達式的集合,這些表達式多是自上次迭代後發生了改變的。若是有檢測到了有改變,那麼$watch函數就會被調用,它一般會把新的值更新到DOM中。
  6. 一旦AngularJS的$digest循環結束,整個執行就會離開AngularJS和Javascript的上下文。這些都是在瀏覽器爲數據改變而進行重渲染以後進行的。

這裏你須要理解一些東西:app

1、$watch list

$watch list的操做發生在指令的link階段。

每次你須要把model上的數據綁定到你的view上時,$watch隊列就會插入一條對應的$watch$watch就是監視的model裏數據變化的東西。

看一下下面的代碼:

<!DOCTYPE html>
<html ng-app="app">
<head>
    <meta charset="utf-8">
    <title></title>

</head>
<body>
<div ng-controller="MainCtrl">
    <input type="text" ng-model="name"/>
    <input type="text" ng-model="age"/>
</div>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"></script>
<script type="text/javascript">
    var app=angular.module('app',[]);

    app.controller('MainCtrl',function ($scope){
        $scope.name='chenwei';
        $scope.age='25';
        $scope.text='world';
    });
</script>
</body>
</html>

$scope上有三個對象:name,age,text。我把name和age綁定到了對應的輸入框上,text沒有綁定,這時候只會向$watch list上插入name和age兩個$watch。

2、$digest loop

 $digest loop 有兩個子循環組成:一個處理evalAsync隊列,另外一個處理$watch隊列。

當瀏覽器接收到能夠被angular context處理的事件時,$digest循環就會觸發。

$digest 會去遍歷$watch list隊列,去檢測裏面的每一項$watch是否發生變化。

這裏須要注意的一點就是,若是有至少一個$watch更新過,這個循環就會再次觸發,直到全部的$watch都沒有變化。這樣就可以保證每一個model都已經不會再變化。記住若是循環超過10次的話,它將會拋出一個異常,防止無限循環。 當$digest循環結束時,DOM相應地變化。 

3、定義本身的watch

$scope對象有一個可供開發者調用的$watch('watchName',fn,boolean)函數

看一個具體的代碼例子:

<!DOCTYPE html>
<html ng-app="app">
<head>
    <meta charset="utf-8">
    <title></title>

</head>
<body>
<div ng-controller="MainCtrl">
    <input type="text" ng-model="name"/>
    <input type="text" ng-model="age"/>
    <p>your name changed {{changeCount}} times</p>
</div>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"></script>
<script type="text/javascript">
    var app=angular.module('app',[]);

    app.controller('MainCtrl',function ($scope){
        $scope.name='chenwei';
        $scope.age='25';
        $scope.text='world';
        $scope.changeCount=0;
        $scope.$watch('name',function (newValue,oldValue){
            if(newValue===oldValue){
                return;
            }
            $scope.changeCount++;
        },true)
    });
</script>
</body>
</html>
View Code

上面的列子裏我用$watch函數去檢測name的值,一旦name的值發生變化,我就把它記錄下來,並經過changeCount亮來通知name變化的次數

相關文章
相關標籤/搜索