亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > JavaScript > 正文

深入學習AngularJS中數據的雙向綁定機制

2019-11-20 10:27:03
字體:
來源:轉載
供稿:網友

Angular JS (Angular.JS) 是一組用來開發Web頁面的框架、模板以及數據綁定和豐富UI組件。它支持整個開發進程,提供web應用的架構,無需進行手工DOM操作。 AngularJS很小,只有60K,兼容主流瀏覽器,與 jQuery 配合良好。雙向數據綁定可能是AngularJS最酷最實用的特性,將MVC的原理展現地淋漓盡致.

AngularJS的工作原理是:HTML模板將會被瀏覽器解析到DOM中, DOM結構成為AngularJS編譯器的輸入。AngularJS將會遍歷DOM模板, 來生成相應的NG指令,所有的指令都負責針對view(即HTML中的ng-model)來設置數據綁定。因此, NG框架是在DOM加載完成之后, 才開始起作用的.

在html中:

<body ng-app="ngApp"> <div ng-controller="ngCtl">  <label ng-model="myLabel"></label>  <input type="text" ng-model="myInput" />  <button ng-model="myButton" ng-click="btnClicked"></button> </div></body>

在js中:

// angular appvar app = angular.module("ngApp", [], function(){ console.log("ng-app : ngApp");});// angular controllerapp.controller("ngCtl", [ '$scope', function($scope){ console.log("ng-controller : ngCtl"); $scope.myLabel = "text for label"; $scope.myInput = "text for input"; $scope.btnClicked = function() {  console.log("Label is " + $scope.myLabel); }}]);

如上,我們在html中先定義一個angular的app,指定一個angular的controller,則該controller會對應于一個作用域(可以用$scope前綴來指定作用域中的屬性和方法等). 則在該ngCtl的作用域內的HTML標簽, 其值或者操作都可以通過$scope的方式跟js中的屬性和方法進行綁定.

這樣, 就實現了NG的雙向數據綁定: 即HTML中呈現的view與AngularJS中的數據是一致的. 修改其一, 則對應的另一端也會相應地發生變化.

這樣的方式,使用起來真的非常方便. 我們僅關心HTML標簽的樣式, 及其對應在js中angular controller作用域下綁定的屬性和方法. 僅此而已, 將眾多復雜的DOM操作全都省略掉了.

這樣的思想,其實跟jQuery的DOM查詢和操作是完全不一樣的, 因此也有很多人建議用AngularJS的時候,不要混合使用jQuery. 當然, 二者各有優劣, 使用哪個就要看自己的選擇了.

NG中的app相當于一個模塊module, 在每個app中可以定義多個controller, 每個controller都會有各自的作用域空間,不會相互干擾.

綁定數據是怎樣生效的
初學AngularJS的人可能會踩到這樣的坑,假設有一個指令:

var app = angular.module("test", []);app.directive("myclick", function() {  return function (scope, element, attr) {    element.on("click", function() {      scope.counter++;    });  };});app.controller("CounterCtrl", function($scope) {  $scope.counter = 0;});<body ng-app="test">  <div ng-controller="CounterCtrl">    <button myclick>increase</button>    <span ng-bind="counter"></span>  </div></body>

這個時候,點擊按鈕,界面上的數字并不會增加。很多人會感到迷惑,因為他查看調試器,發現數據確實已經增加了,Angular不是雙向綁定嗎,為什么數據變化了,界面沒有跟著刷新?

試試在scope.counter++;這句之后加一句scope.digest();再看看是不是好了?

為什么要這么做呢,什么情況下要這么做呢?我們發現第一個例子中并沒有digest,而且,如果你寫了digest,它還會拋出異常,說正在做其他的digest,這是怎么回事?

我們先想想,假如沒有AngularJS,我們想要自己實現這么個功能,應該怎樣?

<!DOCTYPE html><html>  <head>    <meta charset="utf-8" />    <title>two-way binding</title>  </head>  <body onload="init()">    <button ng-click="inc">      increase 1    </button>    <button ng-click="inc2">      increase 2    </button>    <span style="color:red" ng-bind="counter"></span>    <span style="color:blue" ng-bind="counter"></span>    <span style="color:green" ng-bind="counter"></span>    <script type="text/javascript">      /* 數據模型區開始 */      var counter = 0;      function inc() {        counter++;      }      function inc2() {        counter+=2;      }      /* 數據模型區結束 */      /* 綁定關系區開始 */      function init() {        bind();      }      function bind() {        var list = document.querySelectorAll("[ng-click]");        for (var i=0; i<list.length; i++) {          list[i].onclick = (function(index) {            return function() {              window[list[index].getAttribute("ng-click")]();              apply();            };          })(i);        }      }      function apply() {        var list = document.querySelectorAll("[ng-bind='counter']");        for (var i=0; i<list.length; i++) {          list[i].innerHTML = counter;        }      }      /* 綁定關系區結束 */    </script>  </body></html>

可以看到,在這么一個簡單的例子中,我們做了一些雙向綁定的事情。從兩個按鈕的點擊到數據的變更,這個很好理解,但我們沒有直接使用DOM的onclick方法,而是搞了一個ng-click,然后在bind里面把這個ng-click對應的函數拿出來,綁定到onclick的事件處理函數中。為什么要這樣呢?因為數據雖然變更了,但是還沒有往界面上填充,我們需要在此做一些附加操作。

從另外一個方面看,當數據變更的時候,需要把這個變更應用到界面上,也就是那三個span里。但由于Angular使用的是臟檢測,意味著當改變數據之后,你自己要做一些事情來觸發臟檢測,然后再應用到這個數據對應的DOM元素上。問題就在于,怎樣觸發臟檢測?什么時候觸發?

我們知道,一些基于setter的框架,它可以在給數據設值的時候,對DOM元素上的綁定變量作重新賦值。臟檢測的機制沒有這個階段,它沒有任何途徑在數據變更之后立即得到通知,所以只能在每個事件入口中手動調用apply(),把數據的變更應用到界面上。在真正的Angular實現中,這里先進行臟檢測,確定數據有變化了,然后才對界面設值。

所以,我們在ng-click里面封裝真正的click,最重要的作用是為了在之后追加一次apply(),把數據的變更應用到界面上去。

那么,為什么在ng-click里面調用$digest的話,會報錯呢?因為Angular的設計,同一時間只允許一個$digest運行,而ng-click這種內置指令已經觸發了$digest,當前的還沒有走完,所以就出錯了。

$digest和$apply
在Angular中,有$apply和$digest兩個函數,我們剛才是通過$digest來讓這個數據應用到界面上。但這個時候,也可以不用$digest,而是使用$apply,效果是一樣的,那么,它們的差異是什么呢?

最直接的差異是,$apply可以帶參數,它可以接受一個函數,然后在應用數據之后,調用這個函數。所以,一般在集成非Angular框架的代碼時,可以把代碼寫在這個里面調用。

var app = angular.module("test", []);app.directive("myclick", function() {  return function (scope, element, attr) {    element.on("click", function() {      scope.counter++;      scope.$apply(function() {        scope.counter++;      });    });  };});app.controller("CounterCtrl", function($scope) {  $scope.counter = 0;});

除此之外,還有別的區別嗎?

在簡單的數據模型中,這兩者沒有本質差別,但是當有層次結構的時候,就不一樣了??紤]到有兩層作用域,我們可以在父作用域上調用這兩個函數,也可以在子作用域上調用,這個時候就能看到差別了。

對于$digest來說,在父作用域和子作用域上調用是有差別的,但是,對于$apply來說,這兩者一樣。我們來構造一個特殊的示例:

var app = angular.module("test", []);app.directive("increasea", function() {  return function (scope, element, attr) {    element.on("click", function() {      scope.a++;      scope.$digest();    });  };});app.directive("increaseb", function() {  return function (scope, element, attr) {    element.on("click", function() {      scope.b++;      scope.$digest();  //這個換成$apply即可    });  };});app.controller("OuterCtrl", ["$scope", function($scope) {  $scope.a = 1;  $scope.$watch("a", function(newVal) {    console.log("a:" + newVal);  });  $scope.$on("test", function(evt) {    $scope.a++;  });}]);app.controller("InnerCtrl", ["$scope", function($scope) {  $scope.b = 2;  $scope.$watch("b", function(newVal) {    console.log("b:" + newVal);    $scope.$emit("test", newVal);  });}]);<div ng-app="test">  <div ng-controller="OuterCtrl">    <div ng-controller="InnerCtrl">      <button increaseb>increase b</button>      <span ng-bind="b"></span>    </div>    <button increasea>increase a</button>    <span ng-bind="a"></span>  </div></div>

這時候,我們就能看出差別了,在increase b按鈕上點擊,這時候,a跟b的值其實都已經變化了,但是界面上的a沒有更新,直到點擊一次increase a,這時候剛才對a的累加才會一次更新上來。怎么解決這個問題呢?只需在increaseb這個指令的實現中,把$digest換成$apply即可。

當調用$digest的時候,只觸發當前作用域和它的子作用域上的監控,但是當調用$apply的時候,會觸發作用域樹上的所有監控。

因此,從性能上講,如果能確定自己作的這個數據變更所造成的影響范圍,應當盡量調用$digest,只有當無法精確知道數據變更造成的影響范圍時,才去用$apply,很暴力地遍歷整個作用域樹,調用其中所有的監控。

從另外一個角度,我們也可以看到,為什么調用外部框架的時候,是推薦放在$apply中,因為只有這個地方才是對所有數據變更都應用的地方,如果用$digest,有可能臨時丟失數據變更。

臟檢測的利弊
很多人對Angular的臟檢測機制感到不屑,推崇基于setter,getter的觀測機制,在我看來,這只是同一個事情的不同實現方式,并沒有誰完全勝過誰,兩者是各有優劣的。

大家都知道,在循環中批量添加DOM元素的時候,會推薦使用DocumentFragment,為什么呢,因為如果每次都對DOM產生變更,它都要修改DOM樹的結構,性能影響大,如果我們能先在文檔碎片中把DOM結構創建好,然后整體添加到主文檔中,這個DOM樹的變更就會一次完成,性能會提高很多。

同理,在Angular框架里,考慮到這樣的場景:

function TestCtrl($scope) {  $scope.numOfCheckedItems = 0;  var list = [];  for (var i=0; i<10000; i++) {    list.push({      index: i,      checked: false    });  }  $scope.list = list;  $scope.toggleChecked = function(flag) {    for (var i=0; i<list.length; i++) {      list[i].checked = flag;      $scope.numOfCheckedItems++;    }  };}

如果界面上某個文本綁定這個numOfCheckedItems,會怎樣?在臟檢測的機制下,這個過程毫無壓力,一次做完所有數據變更,然后整體應用到界面上。這時候,基于setter的機制就慘了,除非它也是像Angular這樣把批量操作延時到一次更新,否則性能會更低。

所以說,兩種不同的監控方式,各有其優缺點,最好的辦法是了解各自使用方式的差異,考慮出它們性能的差異所在,在不同的業務場景中,避開最容易造成性能瓶頸的用法。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
综合网中文字幕| 在线观看欧美日韩| 亚洲xxxx3d| 久久久在线观看| 51久久精品夜色国产麻豆| 欧美国产亚洲视频| 欧美激情国产日韩精品一区18| 日韩网站免费观看| 亚洲欧美在线一区二区| 欧美亚洲在线播放| 久99九色视频在线观看| 国产成人高潮免费观看精品| 亚洲国产精品成人精品| 亚洲区在线播放| 欧美日韩另类字幕中文| 亚洲黄页网在线观看| 日韩欧美国产中文字幕| 国产精品久久久久久久午夜| 91九色国产社区在线观看| 亚洲国产欧美久久| 欧美高清视频在线观看| 国产精品自拍小视频| 国产免费亚洲高清| 欧美性猛交xxxx乱大交3| 琪琪亚洲精品午夜在线| 亚洲国产成人久久综合一区| 欧美日韩中文字幕| 一区二区在线视频播放| 一道本无吗dⅴd在线播放一区| 亚洲男女性事视频| 国产欧美日韩亚洲精品| 亚洲精品色婷婷福利天堂| 中文欧美在线视频| 国产精品亚洲美女av网站| 国产精品视频1区| 欧美视频不卡中文| 国产精品网站大全| 一本一道久久a久久精品逆3p| 欧美孕妇性xx| 亚洲人成免费电影| 日韩欧美一区视频| 亚洲天堂av高清| 久久久电影免费观看完整版| 亚洲人成免费电影| 美女视频黄免费的亚洲男人天堂| 欧美激情视频给我| 午夜精品久久久久久久白皮肤| 欧美日韩性生活视频| 日韩欧美中文在线| 91精品国产综合久久香蕉| 国产成人涩涩涩视频在线观看| 色多多国产成人永久免费网站| 国产精品99一区| 亚洲精品国产精品自产a区红杏吧| 国产精品入口免费视| 日韩成人激情在线| 亚洲欧洲国产精品| 久久久久久成人精品| 91国产中文字幕| 欧美激情视频网址| 国产ts人妖一区二区三区| 91在线观看免费| 日韩av免费在线播放| 国产精品美乳一区二区免费| 精品毛片网大全| 亚洲国产精品大全| 2023亚洲男人天堂| 美女av一区二区三区| 欧美成人中文字幕在线| 成人激情视频免费在线| 色狠狠久久aa北条麻妃| 国产自摸综合网| 国产精品久久久久aaaa九色| 中文字幕一区电影| 91国产视频在线播放| 日韩免费高清在线观看| 亚洲精品欧美日韩专区| 亚洲精品一区av在线播放| 在线观看免费高清视频97| 国产精品国内视频| 欧美精品国产精品日韩精品| 啪一啪鲁一鲁2019在线视频| 色狠狠久久aa北条麻妃| 久久躁狠狠躁夜夜爽| 亚洲视频专区在线| 亚洲一区二区中文字幕| 国产视频观看一区| 欧美日韩国产限制| 国产精品影院在线观看| 日韩成人中文字幕| 81精品国产乱码久久久久久| 欧美极品少妇xxxxx| 亚洲精品99久久久久| 亚洲男人天堂古典| 欧美中文在线观看国产| 国产精品午夜一区二区欲梦| 91久久精品视频| 亚洲码在线观看| 高清亚洲成在人网站天堂| 日韩在线激情视频| 国产日韩中文在线| 日韩女优人人人人射在线视频| 国产美女扒开尿口久久久| 国产精品亚洲一区二区三区| 中文字幕亚洲综合久久| **欧美日韩vr在线| 91精品国产九九九久久久亚洲| 尤物九九久久国产精品的分类| 久久亚洲电影天堂| 九九热99久久久国产盗摄| 亚洲第一页自拍| 欧美亚洲午夜视频在线观看| 亚洲性69xxxbbb| 亚洲国产欧美自拍| 欧美小视频在线| 日本国产一区二区三区| 91在线视频免费| 97国产suv精品一区二区62| 亚洲精品永久免费精品| 欧美富婆性猛交| 亚洲第五色综合网| 中文字幕亚洲欧美日韩2019| 91亚洲精华国产精华| 午夜精品99久久免费| 国产91久久婷婷一区二区| 国产成人久久久精品一区| 黄色一区二区三区| 国产精品扒开腿做爽爽爽视频| 久久久久久久爱| 欧美xxxx18性欧美| 日本久久久久久| 日韩视频亚洲视频| 久久久91精品| 欧美日韩综合视频| 色婷婷**av毛片一区| 亚洲va国产va天堂va久久| 欧美精品久久久久久久| 欧美一级电影久久| 国产精欧美一区二区三区| 91日韩在线播放| 亚洲色图35p| 亚洲小视频在线| 久久久久久中文| 亚洲视频在线观看视频| 色先锋久久影院av| 国产美女精品视频免费观看| 欧美激情亚洲综合一区| 欧洲亚洲免费在线| 久色乳综合思思在线视频| 亚洲欧美日韩精品久久| 日本a级片电影一区二区| 韩国日本不卡在线| 亚洲第一精品夜夜躁人人爽| 欧美一级电影免费在线观看| 欧美成人在线网站| 久久久999精品视频| 91精品免费看| 日韩网站免费观看| 成人网欧美在线视频| 亚洲人成网站777色婷婷| 亚洲成人av在线| 国产精品久久久久久久电影| 国产亚洲视频在线观看| 国产久一一精品|