首頁 php教程 PHP开发 AngularJS之資料綁定原理詳解

AngularJS之資料綁定原理詳解

Dec 08, 2016 am 10:22 AM
angularjs

這篇文章主要是寫給新手的,是給那些剛開始接觸Angular,並且想了解資料幫定是如何運作的人。如果你已經對Angular比較了解了,那強烈建議你直接去閱讀原始碼。

Angular用戶都想知道資料綁定是怎麼實現的。你可能會看到各種各樣的詞彙:$watch,$apply,$digest,dirty-checking...它們是什麼?它們是如何運作的呢?這裡我想回答這些問題,其實它們在官方的文檔裡都已經回答了,但是我還是想把它們結合在一起來講,但是我只是用一種簡單的方法來講解,如果要想了解技術細節,查看原始碼。

讓我們從頭開始。

瀏覽器事件循環和Angular.js擴充

我們的瀏覽器一直在等待事件,例如使用者互動。假如你點選一個按鈕或是在輸入框裡輸入東西,事件的回呼函數就會在JavaScript解釋器裡執行,然後你就可以做任何DOM操作,等回呼函數執行完畢時,瀏覽器就會相應地對DOM做出變化。 Angular拓展了這個事件循環,產生一個有時成為angular context的執行環境(記住,這是個重要的概念),為了解釋什麼是context以及它如何運作,我們還需要解釋更多的概念。

$watch 佇列($watch list)

每次你綁定一些東西到你的UI上時你就會往$watch隊列裡插入一條$watch。想像一下$watch就是那個可以偵測它監視的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(&#39;MainCtrl&#39;, function($scope) {
 $scope.foo = "Foo";
 $scope.world = "World";
});
登入後複製

   

rr

   
Hello, {{ World }}
登入後複製

   

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

再看下面的例子:

controllers. js

app.controller(&#39;MainCtrl&#39;, 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上的資料都會產生一個$watch。對,那這寫$watch是什麼時候產生的呢? 當我們的模版加載完成時,也就是在linking階段(Angular分為compile階段和linking階段---譯者註),Angular解釋器會尋找每個directive,然後產生每個需要的$watch。聽起來不錯哈,但是,然後呢?

$digest循環


還記得我前面提到的擴充的事件循環嗎?當瀏覽器接收到可以被angular context處理的事件時,$digest循環就會觸發。這個循環是由兩個較小的循環組合起來的。一個處理evalAsync佇列,另一個處理$watch佇列,這個也是本篇部落格的主題。 這個是處理什麼的呢? $digest將會遍歷我們的$watch,然後問:

嘿,$watch,你的值是什麼?
是9。
好的,它改過嗎?
沒有,先生。
(這個變數沒變過,那下一個)
你呢,你的值是多少?
報告,是Foo。
剛才改變過沒?

改變過,剛才是Bar。

(很好,我們有DOM需要更新了)

繼續詢問知道$watch隊列都檢查過。

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

例如: controllers.js

app.controller(&#39;MainCtrl&#39;, function() {
 $scope.name = "Foo";
 $scope.changeFoo = function() {
   $scope.name = "Bar";
 }
});
登入後複製

   

index.html

{{ name }}
<button ng-click="changeFoo()">Change the name</button>
登入後複製

   

我們按下按鈕

瀏覽器接收到一個事件,進入angular context(後面會解釋為什麼)。

$digest循環開始執行,查詢每個$watch是否變化。

由於監視$scope.name的$watch報告了變化,它會強制再執行一次$digest循環。

新的$digest循環沒有偵測到變化。

瀏覽器拿回控制權,更新與$scope.name新值對應部分的DOM。

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

透過$apply來進入angular context🎜🎜誰決定什麼事件進入angular context,而哪些又不進入呢? $apply! 🎜

如果当事件触发时,你调用$apply,它会进入angular context,如果没有调用就不会进入。现在你可能会问:刚才的例子里我也没有调用$apply啊,为什么?Angular为了做了!因此你点击带有ng-click的元素时,时间就会被封装到一个$apply调用。如果你有一个ng-model="foo"的输入框,然后你敲一个f,事件就会这样调用$apply("foo = 'f';")。

Angular什么时候不会自动为我们$apply呢?

这是Angular新手共同的痛处。为什么我的jQuery不会更新我绑定的东西呢?因为jQuery没有调用$apply,事件没有进入angular context,$digest循环永远没有执行。

我们来看一个有趣的例子:

假设我们有下面这个directive和controller

app.js

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

它将foo和bar从controller里绑定到一个list里面,每次点击这个元素的时候,foo和bar都会自增1。

那我们点击元素的时候会发生什么呢?我们能看到更新吗?答案是否定的。因为点击事件是一个没有封装到$apply里面的常见的事件,这意味着我们会失去我们的计数吗?不会

真正的结果是:$scope确实改变了,但是没有强制$digest循环,监视foo 和bar的$watch没有执行。也就是说如果我们自己执行一次$apply那么这些$watch就会看见这些变化,然后根据需要更新DOM。

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

如果我们点击这个directive(蓝色区域),我们看不到任何变化,但是我们点击按钮时,点击数就更新了。如刚才说的,在这个directive上点击时我们不会触发$digest循环,但是当按钮被点击时,ng-click会调用$apply,然后就会执行$digest循环,于是所有的$watch都会被检查,当然就包括我们的foo和bar的$watch了。

现在你在想那并不是你想要的,你想要的是点击蓝色区域的时候就更新点击数。很简单,执行一下$apply就可以了:

element.bind(&#39;click&#39;, function() {
 scope.foo++;
 scope.bar++;
 scope.$apply();
});
登入後複製

$apply是我们的$scope(或者是direcvie里的link函数中的scope)的一个函数,调用它会强制一次$digest循环(除非当前正在执行循环,这种情况下会抛出一个异常,这是我们不需要在那里执行$apply的标志)。

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

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

element.bind(&#39;click&#39;, function() {
 scope.$apply(function() {
   scope.foo++;
   scope.bar++;
 });
})
登入後複製

有什么不一样的?差别就是在第一个版本中,我们是在angular context的外面更新的数据,如果有发生错误,Angular永远不知道。很明显在这个像个小玩具的例子里面不会出什么大错,但是想象一下我们如果有个alert框显示错误给用户,然后我们有个第三方的库进行一个网络调用然后失败了,如果我们不把它封装进$apply里面,Angular永远不会知道失败了,alert框就永远不会弹出来了。

因此,如果你想使用一个jQuery插件,并且要执行$digest循环来更新你的DOM的话,要确保你调用了$apply。

有时候我想多说一句的是有些人在不得不调用$apply时会“感觉不妙”,因为他们会觉得他们做错了什么。其实不是这样的,Angular不是什么魔术师,他也不知道第三方库想要更新绑定的数据。

使用$watch来监视你自己的东西

你已经知道了我们设置的任何绑定都有一个它自己的$watch,当需要时更新DOM,但是我们如果要自定义自己的watches呢?简单

来看个例子:

app.js

app.controller(&#39;MainCtrl&#39;, function($scope) {
 $scope.name = "Angular";
 $scope.updated = -1;
 $scope.$watch(&#39;name&#39;, function() {
  $scope.updated++;
 });
});
登入後複製

index.html

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

这就是我们创造一个新的$watch的方法。第一个参数是一个字符串或者函数,在这里是只是一个字符串,就是我们要监视的变量的名字,在这里,$scope.name(注意我们只需要用name)。第二个参数是当$watch说我监视的表达式发生变化后要执行的。我们要知道的第一件事就是当controller执行到这个$watch时,它会立即执行一次,因此我们设置updated为-1。

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

例子2:

app.js

app.controller(&#39;MainCtrl&#39;, function($scope) {
 $scope.name = "Angular";
 $scope.updated = 0;
 $scope.$watch(&#39;name&#39;, 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的第二个参数接受两个参数,新值和旧值。我们可以用他们来略过第一次的执行。通常你不需要略过第一次执行,但在这个例子里面你是需要的。灵活点嘛少年。

例子3:

app.js

app.controller(&#39;MainCtrl&#39;, function($scope) {
 $scope.user = { name: "Fox" };
 $scope.updated = 0;
 $scope.$watch(&#39;user&#39;, 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>
登入後複製
登入後複製

我们想要监视$scope.user对象里的任何变化,和以前一样这里只是用一个对象来代替前面的字符串。

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

呃?没用,为啥?因为$watch默认是比较两个对象所引用的是否相同,在例子1和2里面,每次更改$scope.name都会创建一个新的基本变量,因此$watch会执行,因为对这个变量的引用已经改变了。在上面的例子里,我们在监视$scope.user,当我们改变$scope.user.name时,对$scope.user的引用是不会改变的,我们只是每次创建了一个新的$scope.user.name,但是$scope.user永远是一样的。

例子4:

app.js

app.controller(&#39;MainCtrl&#39;, function($scope) {
 $scope.user = { name: "Fox" };
 $scope.updated = 0;
 $scope.$watch(&#39;user&#39;, 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

现在有用了吧!因为我们对$watch加入了第三个参数,它是一个bool类型的参数,表示的是我们比较的是对象的值而不是引用。由于当我们更新$scope.user.name时$scope.user也会改变,所以能够正确触发。

关于$watch还有很多tips&tricks,但是这些都是基础。

总结

好吧,我希望你们已经学会了在Angular中数据绑定是如何工作的。我猜想你的第一印象是dirty-checking很慢,好吧,其实是不对的。它像闪电般快。但是,是的,如果你在一个模版里有2000-3000个watch,它会开始变慢。但是我觉得如果你达到这个数量级,就可以找个用户体验专家咨询一下了

无论如何,随着ECMAScript6的到来,在Angular未来的版本里我们将会有Object.observe那样会极大改善$digest循环的速度。同时未来的文章也会涉及一些tips&tricks。

另一方面,这个主题并不容易,如果你发现我落下了什么重要的东西或者有什么东西完全错了,请指正(原文是在GITHUB上PR 或报告issue)

希望本文所述对大家AngularJS程序设计有所帮助。


本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

<🎜>:泡泡膠模擬器無窮大 - 如何獲取和使用皇家鑰匙
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
北端:融合系統,解釋
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
Mandragora:巫婆樹的耳語 - 如何解鎖抓鉤
3 週前 By 尊渡假赌尊渡假赌尊渡假赌

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

熱門話題

Java教學
1665
14
CakePHP 教程
1424
52
Laravel 教程
1322
25
PHP教程
1270
29
C# 教程
1250
24
2022年最新5款的angularjs教學從入門到精通 2022年最新5款的angularjs教學從入門到精通 Jun 15, 2017 pm 05:50 PM

Javascript 是一個非常有個性的語言. 無論是從程式碼的組織, 還是程式碼的程式設計範式, 或物件導向理論都獨具一格. 而很早就在爭論的Javascript 是不是物件導向語言這個問題, 顯然已有答案. 但是, 即使Javascript 叱吒風雲二十年, 如果想要看懂jQuery, Angularjs, 甚至是React 等流行框架, 觀看《黑馬雲課堂JavaScript 高級框架設計視頻教程》就對了。

使用PHP和AngularJS建立響應式網站,提供優質的使用者體驗 使用PHP和AngularJS建立響應式網站,提供優質的使用者體驗 Jun 27, 2023 pm 07:37 PM

在現今資訊時代,網站已成為人們獲取資訊和交流的重要工具。一個響應式的網站能夠適應各種設備,為使用者提供優質的體驗,成為了現代網站開發的熱點。本篇文章將介紹如何使用PHP和AngularJS建立響應式網站,進而提供優質的使用者體驗。 PHP介紹PHP是一種開源的伺服器端程式語言,非常適合Web開發。 PHP具有許多優點,如易於學習、跨平台、豐富的工具庫、開發效

使用PHP和AngularJS建立Web應用 使用PHP和AngularJS建立Web應用 May 27, 2023 pm 08:10 PM

隨著網路的不斷發展,Web應用已成為企業資訊化建設的重要組成部分,也是現代化工作的必要手段。為了讓Web應用能夠方便開發、維護和擴展,開發人員需要選擇適合自己開發需求的技術框架和程式語言。 PHP和AngularJS是兩種非常流行的Web開發技術,它們分別是伺服器端和客戶端的解決方案,透過結合使用可以大大提高Web應用的開發效率和使用體驗。 PHP的優勢PHP

使用Flask和AngularJS建立單頁Web應用程式 使用Flask和AngularJS建立單頁Web應用程式 Jun 17, 2023 am 08:49 AM

隨著Web技術的快速發展,單頁Web應用程式(SinglePageApplication,SPA)已成為越來越流行的Web應用程式模型。相較於傳統的多頁Web應用程序,SPA的最大優勢在於使用者感受更加流暢,同時伺服器端的運算壓力也大幅減少。在本文中,我們將介紹如何使用Flask和AngularJS來建構一個簡單的SPA。 Flask是一款輕量級的Py

AngularJS基礎入門介紹 AngularJS基礎入門介紹 Apr 21, 2018 am 10:37 AM

這篇文章介紹的內容是關於AngularJS基礎入門介紹,有著一定的參考價值,現在分享給大家,有需要的朋友可以參考一下。

如何使用PHP和AngularJS進行前端開發 如何使用PHP和AngularJS進行前端開發 May 11, 2023 pm 05:18 PM

隨著網路的普及和發展,前端開發已變得越來越重要。身為前端開發人員,我們需要了解並掌握各種開發工具和技術。其中,PHP和AngularJS是兩種非常有用且受歡迎的工具。在本文中,我們將介紹如何使用這兩種工具進行前端開發。一、PHP介紹PHP是一種流行的開源伺服器端腳本語言,它適用於Web開發,可以在Web伺服器和各種作業系統上運作。 PHP的優點是簡單、快速、便

如何在PHP編程中使用AngularJS? 如何在PHP編程中使用AngularJS? Jun 12, 2023 am 09:40 AM

隨著Web應用程式的普及,前端框架AngularJS變得越來越受歡迎。 AngularJS是一個由Google開發的JavaScript框架,它可以幫助你建立具有動態Web應用程式功能的網頁應用程式。另一方面,對於後端編程,PHP是非常受歡迎的程式語言。如果您正在使用PHP進行伺服器端編程,那麼結合AngularJS使用PHP將可以為您的網站帶來更多的動態效

使用PHP和AngularJS開發一個線上文件管理平台,方便文件管理 使用PHP和AngularJS開發一個線上文件管理平台,方便文件管理 Jun 27, 2023 pm 01:34 PM

隨著網路的普及,越來越多的人使用網路進行檔案傳輸和分享。然而,由於各種原因,使用傳統的FTP等方式進行檔案管理無法滿足現代使用者的需求。因此,建立一個易用、高效、安全的線上文件管理平台已成為了一種趨勢。本文介紹的線上文件管理平台,基於PHP和AngularJS,能夠方便地進行文件上傳、下載、編輯、刪除等操作,並且提供了一系列強大的功能,例如文件共享、搜尋、

See all articles