欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

AngularJS入門教程之?dāng)?shù)據(jù)綁定原理詳解

 更新時間:2016年11月02日 12:22:27   作者:Rongbo_J  
這篇文章主要介紹了AngularJS數(shù)據(jù)綁定原理,較為詳細(xì)的分析了AngularJS數(shù)據(jù)綁定的原理、使用技巧與相關(guān)注意事項,需要的朋友可以參考下

本文實例講述了AngularJS數(shù)據(jù)綁定原理。分享給大家供大家參考,具體如下:

這篇文章主要是寫給新手的,是給那些剛剛開始接觸Angular,并且想了解數(shù)據(jù)幫定是如何工作的人。如果你已經(jīng)對Angular比較了解了,那強(qiáng)烈建議你直接去閱讀源代碼。

Angular用戶都想知道數(shù)據(jù)綁定是怎么實現(xiàn)的。你可能會看到各種各樣的詞匯:$watch,$apply,$digest,dirty-checking...它們是什么?它們是如何工作的呢?這里我想回答這些問題,其實它們在官方的文檔里都已經(jīng)回答了,但是我還是想把它們結(jié)合在一起來講,但是我只是用一種簡單的方法來講解,如果要想了解技術(shù)細(xì)節(jié),查看源代碼。

讓我們從頭開始吧。

瀏覽器事件循環(huán)和Angular.js擴(kuò)展

我們的瀏覽器一直在等待事件,比如用戶交互。假如你點擊一個按鈕或者在輸入框里輸入東西,事件的回調(diào)函數(shù)就會在JavaScript解釋器里執(zhí)行,然后你就可以做任何DOM操作,等回調(diào)函數(shù)執(zhí)行完畢時,瀏覽器就會相應(yīng)地對DOM做出變化。 Angular拓展了這個事件循環(huán),生成一個有時成為angular context的執(zhí)行環(huán)境(記住,這是個重要的概念),為了解釋什么是context以及它如何工作,我們還需要解釋更多的概念。

$watch 隊列($watch list)

每次你綁定一些東西到你的UI上時你就會往$watch隊列里插入一條$watch。想象一下$watch就是那個可以檢測它監(jiān)視的model里時候有變化的東西。例如你有如下的代碼

index.html

User: <input type="text" ng-model="user" />
Password: <input type="password" ng-model="pass" />

在這里我們有個$scope.user,他被綁定在了第一個輸入框上,還有個$scope.pass,它被綁定在了第二個輸入框上,然后我們在$watch list里面加入兩個$watch

接著看下面的例子:

controllers.js

app.controller('MainCtrl', function($scope) {
 $scope.foo = "Foo";
 $scope.world = "World";
});

index.html

Hello, {{ World }}

這里,即便我們在$scope上添加了兩個東西,但是只有一個綁定在了UI上,因此在這里只生成了一個$watch.

再看下面的例子:

controllers.js

app.controller('MainCtrl', function($scope) {
 $scope.people = [...];
});

index.html

<ul>
 <li ng-repeat="person in people">
   {{person.name}} - {{person.age}}
 </li>
</ul>

這里又生成了多少個$watch呢?每個person有兩個(一個name,一個age),然后ng-repeat又有一個,因此10個person一共是(2 * 10) +1,也就是說有21個$watch。 因此,每一個綁定到了UI上的數(shù)據(jù)都會生成一個$watch。對,那這寫$watch是什么時候生成的呢? 當(dāng)我們的模版加載完畢時,也就是在linking階段(Angular分為compile階段和linking階段---譯者注),Angular解釋器會尋找每個directive,然后生成每個需要的$watch。聽起來不錯哈,但是,然后呢?

$digest循環(huán)

還記得我前面提到的擴(kuò)展的事件循環(huán)嗎?當(dāng)瀏覽器接收到可以被angular context處理的事件時,$digest循環(huán)就會觸發(fā)。這個循環(huán)是由兩個更小的循環(huán)組合起來的。一個處理evalAsync隊列,另一個處理$watch隊列,這個也是本篇博文的主題。 這個是處理什么的呢?$digest將會遍歷我們的$watch,然后詢問:

嘿,$watch,你的值是什么?
是9。
好的,它改變過嗎?
沒有,先生。
(這個變量沒變過,那下一個)
你呢,你的值是多少?
報告,是Foo。
剛才改變過沒?
改變過,剛才是Bar。
(很好,我們有DOM需要更新了)
繼續(xù)詢問知道$watch隊列都檢查過。

這就是所謂的dirty-checking。既然所有的$watch都檢查完了,那就要問了:有沒有$watch更新過?如果有至少一個更新過,這個循環(huán)就會再次觸發(fā),直到所有的$watch都沒有變化。這樣就能夠保證每個model都已經(jīng)不會再變化。記住如果循環(huán)超過10次的話,它將會拋出一個異常,防止無限循環(huán)。 當(dāng)$digest循環(huán)結(jié)束時,DOM相應(yīng)地變化。

例如: controllers.js

app.controller('MainCtrl', function() {
 $scope.name = "Foo";
 $scope.changeFoo = function() {
   $scope.name = "Bar";
 }
});

index.html

{{ name }}
<button ng-click="changeFoo()">Change the name</button>

這里我們有一個$watch因為ng-click不生成$watch(函數(shù)是不會變的)。

我們按下按鈕

瀏覽器接收到一個事件,進(jìn)入angular context(后面會解釋為什么)。

$digest循環(huán)開始執(zhí)行,查詢每個$watch是否變化。

由于監(jiān)視$scope.name的$watch報告了變化,它會強(qiáng)制再執(zhí)行一次$digest循環(huán)。

新的$digest循環(huán)沒有檢測到變化。

瀏覽器拿回控制權(quán),更新與$scope.name新值相應(yīng)部分的DOM。

這里很重要的(也是許多人的很蛋疼的地方)是每一個進(jìn)入angular context的事件都會執(zhí)行一個$digest循環(huán),也就是說每次我們輸入一個字母循環(huán)都會檢查整個頁面的所有$watch。

通過$apply來進(jìn)入angular context

誰決定什么事件進(jìn)入angular context,而哪些又不進(jìn)入呢?$apply!

如果當(dāng)事件觸發(fā)時,你調(diào)用$apply,它會進(jìn)入angular context,如果沒有調(diào)用就不會進(jìn)入?,F(xiàn)在你可能會問:剛才的例子里我也沒有調(diào)用$apply啊,為什么?Angular為了做了!因此你點擊帶有ng-click的元素時,時間就會被封裝到一個$apply調(diào)用。如果你有一個ng-model="foo"的輸入框,然后你敲一個f,事件就會這樣調(diào)用$apply("foo = 'f';")。

Angular什么時候不會自動為我們$apply呢?

這是Angular新手共同的痛處。為什么我的jQuery不會更新我綁定的東西呢?因為jQuery沒有調(diào)用$apply,事件沒有進(jìn)入angular context,$digest循環(huán)永遠(yuǎn)沒有執(zhí)行。

我們來看一個有趣的例子:

假設(shè)我們有下面這個directive和controller

app.js

app.directive('clickable', function() {
return {
 restrict: "E",
 scope: {
  foo: '=',
  bar: '='
 },
 template: '<ul style="background-color: lightblue"><li>{{foo}}</li><li>{{bar}}</li></ul>',
 link: function(scope, element, attrs) {
  element.bind('click', function() {
   scope.foo++;
   scope.bar++;
  });
 }
}
});
app.controller('MainCtrl', function($scope) {
 $scope.foo = 0;
 $scope.bar = 0;
});

它將foo和bar從controller里綁定到一個list里面,每次點擊這個元素的時候,foo和bar都會自增1。

那我們點擊元素的時候會發(fā)生什么呢?我們能看到更新嗎?答案是否定的。因為點擊事件是一個沒有封裝到$apply里面的常見的事件,這意味著我們會失去我們的計數(shù)嗎?不會

真正的結(jié)果是:$scope確實改變了,但是沒有強(qiáng)制$digest循環(huán),監(jiān)視foo 和bar的$watch沒有執(zhí)行。也就是說如果我們自己執(zhí)行一次$apply那么這些$watch就會看見這些變化,然后根據(jù)需要更新DOM。

試試看吧:http://jsbin.com/opimat/2/

如果我們點擊這個directive(藍(lán)色區(qū)域),我們看不到任何變化,但是我們點擊按鈕時,點擊數(shù)就更新了。如剛才說的,在這個directive上點擊時我們不會觸發(fā)$digest循環(huán),但是當(dāng)按鈕被點擊時,ng-click會調(diào)用$apply,然后就會執(zhí)行$digest循環(huán),于是所有的$watch都會被檢查,當(dāng)然就包括我們的foo和bar的$watch了。

現(xiàn)在你在想那并不是你想要的,你想要的是點擊藍(lán)色區(qū)域的時候就更新點擊數(shù)。很簡單,執(zhí)行一下$apply就可以了:

element.bind('click', function() {
 scope.foo++;
 scope.bar++;
 scope.$apply();
});

$apply是我們的$scope(或者是direcvie里的link函數(shù)中的scope)的一個函數(shù),調(diào)用它會強(qiáng)制一次$digest循環(huán)(除非當(dāng)前正在執(zhí)行循環(huán),這種情況下會拋出一個異常,這是我們不需要在那里執(zhí)行$apply的標(biāo)志)。

試試看:http://jsbin.com/opimat/3/edit

有用啦!但是有一種更好的使用$apply的方法:

element.bind('click', function() {
 scope.$apply(function() {
   scope.foo++;
   scope.bar++;
 });
})

有什么不一樣的?差別就是在第一個版本中,我們是在angular context的外面更新的數(shù)據(jù),如果有發(fā)生錯誤,Angular永遠(yuǎn)不知道。很明顯在這個像個小玩具的例子里面不會出什么大錯,但是想象一下我們?nèi)绻袀€alert框顯示錯誤給用戶,然后我們有個第三方的庫進(jìn)行一個網(wǎng)絡(luò)調(diào)用然后失敗了,如果我們不把它封裝進(jìn)$apply里面,Angular永遠(yuǎn)不會知道失敗了,alert框就永遠(yuǎn)不會彈出來了。

因此,如果你想使用一個jQuery插件,并且要執(zhí)行$digest循環(huán)來更新你的DOM的話,要確保你調(diào)用了$apply。

有時候我想多說一句的是有些人在不得不調(diào)用$apply時會“感覺不妙”,因為他們會覺得他們做錯了什么。其實不是這樣的,Angular不是什么魔術(shù)師,他也不知道第三方庫想要更新綁定的數(shù)據(jù)。

使用$watch來監(jiān)視你自己的東西

你已經(jīng)知道了我們設(shè)置的任何綁定都有一個它自己的$watch,當(dāng)需要時更新DOM,但是我們?nèi)绻远x自己的watches呢?簡單

來看個例子:

app.js

app.controller('MainCtrl', function($scope) {
 $scope.name = "Angular";
 $scope.updated = -1;
 $scope.$watch('name', function() {
  $scope.updated++;
 });
});

index.html

<body ng-controller="MainCtrl">
 <input ng-model="name" />
 Name updated: {{updated}} times.
</body>

這就是我們創(chuàng)造一個新的$watch的方法。第一個參數(shù)是一個字符串或者函數(shù),在這里是只是一個字符串,就是我們要監(jiān)視的變量的名字,在這里,$scope.name(注意我們只需要用name)。第二個參數(shù)是當(dāng)$watch說我監(jiān)視的表達(dá)式發(fā)生變化后要執(zhí)行的。我們要知道的第一件事就是當(dāng)controller執(zhí)行到這個$watch時,它會立即執(zhí)行一次,因此我們設(shè)置updated為-1。

試試看:http://jsbin.com/ucaxan/1/edit

例子2:

app.js

app.controller('MainCtrl', function($scope) {
 $scope.name = "Angular";
 $scope.updated = 0;
 $scope.$watch('name', function(newValue, oldValue) {
  if (newValue === oldValue) { return; } // AKA first run
  $scope.updated++;
 });
});

index.html

<body ng-controller="MainCtrl">
 <input ng-model="name" />
 Name updated: {{updated}} times.
</body>

watch的第二個參數(shù)接受兩個參數(shù),新值和舊值。我們可以用他們來略過第一次的執(zhí)行。通常你不需要略過第一次執(zhí)行,但在這個例子里面你是需要的。靈活點嘛少年。

例子3:

app.js

app.controller('MainCtrl', function($scope) {
 $scope.user = { name: "Fox" };
 $scope.updated = 0;
 $scope.$watch('user', function(newValue, oldValue) {
  if (newValue === oldValue) { return; }
  $scope.updated++;
 });
});

index.html

<body ng-controller="MainCtrl">
 <input ng-model="user.name" />
 Name updated: {{updated}} times.
</body>

我們想要監(jiān)視$scope.user對象里的任何變化,和以前一樣這里只是用一個對象來代替前面的字符串。

試試看:http://jsbin.com/ucaxan/3/edit

呃?沒用,為啥?因為$watch默認(rèn)是比較兩個對象所引用的是否相同,在例子1和2里面,每次更改$scope.name都會創(chuàng)建一個新的基本變量,因此$watch會執(zhí)行,因為對這個變量的引用已經(jīng)改變了。在上面的例子里,我們在監(jiān)視$scope.user,當(dāng)我們改變$scope.user.name時,對$scope.user的引用是不會改變的,我們只是每次創(chuàng)建了一個新的$scope.user.name,但是$scope.user永遠(yuǎn)是一樣的。

例子4:

app.js

app.controller('MainCtrl', function($scope) {
 $scope.user = { name: "Fox" };
 $scope.updated = 0;
 $scope.$watch('user', function(newValue, oldValue) {
  if (newValue === oldValue) { return; }
  $scope.updated++;
 }, true);
});

index.html

<body ng-controller="MainCtrl">
 <input ng-model="user.name" />
 Name updated: {{updated}} times.
</body>

試試看:http://jsbin.com/ucaxan/4/edit

現(xiàn)在有用了吧!因為我們對$watch加入了第三個參數(shù),它是一個bool類型的參數(shù),表示的是我們比較的是對象的值而不是引用。由于當(dāng)我們更新$scope.user.name時$scope.user也會改變,所以能夠正確觸發(fā)。

關(guān)于$watch還有很多tips&tricks,但是這些都是基礎(chǔ)。

總結(jié)

好吧,我希望你們已經(jīng)學(xué)會了在Angular中數(shù)據(jù)綁定是如何工作的。我猜想你的第一印象是dirty-checking很慢,好吧,其實是不對的。它像閃電般快。但是,是的,如果你在一個模版里有2000-3000個watch,它會開始變慢。但是我覺得如果你達(dá)到這個數(shù)量級,就可以找個用戶體驗專家咨詢一下了

無論如何,隨著ECMAScript6的到來,在Angular未來的版本里我們將會有Object.observe那樣會極大改善$digest循環(huán)的速度。同時未來的文章也會涉及一些tips&tricks。

另一方面,這個主題并不容易,如果你發(fā)現(xiàn)我落下了什么重要的東西或者有什么東西完全錯了,請指正(原文是在GITHUB上PR 或報告issue)

希望本文所述對大家AngularJS程序設(shè)計有所幫助。

相關(guān)文章

  • 在Angular中使用innerHTML屬性綁定的方法

    在Angular中使用innerHTML屬性綁定的方法

    Angular 2+ 支持使用 [innerHTML] 屬性綁定來渲染 HTML,如果你使用插值,它會被視為字符串,本文將介紹如何使用 [innerHTML] 以及一些注意事項,需要的朋友可以參考下
    2024-02-02
  • Angular?項目路徑添加指定的訪問前綴

    Angular?項目路徑添加指定的訪問前綴

    這篇文章主要為大家介紹了Angular?項目路徑添加指定的訪問前綴方法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-03-03
  • Angular4 組件通訊方法大全(推薦)

    Angular4 組件通訊方法大全(推薦)

    這篇文章主要介紹了Angular4 組件通訊方法大全(推薦),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-07-07
  • Angular2 自定義表單驗證器的實現(xiàn)方法

    Angular2 自定義表單驗證器的實現(xiàn)方法

    這篇文章主要介紹了Angular2 自定義表單驗證器的實現(xiàn)方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-12-12
  • 詳解angularjs 關(guān)于ui-router分層使用

    詳解angularjs 關(guān)于ui-router分層使用

    本篇文章主要介紹了詳解angularjs 關(guān)于ui-router分層使用,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-06-06
  • Angular中的ng-template及angular 使用ngTemplateOutlet 指令的方法

    Angular中的ng-template及angular 使用ngTemplateOutlet 指令的方法

    ng-template 是用來定義模板的,當(dāng)使用ng-template定義好一個模板之后,可以用ng-container和templateOutlet指令來進(jìn)行使用。這篇文章給大家介紹了Angular中的ng-templateangular及使用 ngTemplateOutlet 指令的方法,需要的朋友參考下吧
    2018-08-08
  • BootStrap+Angularjs+NgDialog實現(xiàn)模式對話框

    BootStrap+Angularjs+NgDialog實現(xiàn)模式對話框

    在完成一個后臺管理系統(tǒng)時,需要用表格顯示注冊用戶的信息。但是用戶地址太長了,不好顯示。所以想做一個模式對話框,點擊詳細(xì)地址按鈕時,彈出對話框,顯示地址。下面小編給大家分享下實現(xiàn)方法,一起看下吧
    2016-08-08
  • angularjs中回車鍵觸發(fā)某一事件的方法

    angularjs中回車鍵觸發(fā)某一事件的方法

    下面小編就為大家?guī)硪黄猘ngularjs中回車鍵觸發(fā)某一事件的方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-04-04
  • AngularJS基礎(chǔ) ng-cloak 指令簡單示例

    AngularJS基礎(chǔ) ng-cloak 指令簡單示例

    本文主要介紹AngularJS ng-cloak 指令,這里幫大家整理了ng-clock指令的基礎(chǔ)資料,和簡單的代碼實例及效果圖,學(xué)習(xí)AngularJS指令的朋友可以參考下
    2016-08-08
  • angular2實現(xiàn)統(tǒng)一的http請求頭方法

    angular2實現(xiàn)統(tǒng)一的http請求頭方法

    今天小編就為大家分享一篇angular2實現(xiàn)統(tǒng)一的http請求頭方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-08-08

最新評論