淺談AngularJs 雙向綁定原理(數(shù)據(jù)綁定機(jī)制)
那么什么是雙向綁定,下面簡(jiǎn)單進(jìn)行講解。
首先我們要理解數(shù)據(jù)綁定。我們看到的網(wǎng)站頁(yè)面中,是由數(shù)據(jù)和設(shè)計(jì)兩部分組合而成。將設(shè)計(jì)轉(zhuǎn)換成瀏覽器能理解的語(yǔ)言,便是html和css主要做的工作。而將數(shù)據(jù)顯示在頁(yè)面上,并且有一定的交互效果(比如點(diǎn)擊等用戶(hù)操作及對(duì)應(yīng)的頁(yè)面反應(yīng))則是js主要完成的工作。很多時(shí)候我們不可能每次更新數(shù)據(jù)便刷新頁(yè)面(get請(qǐng)求),而是通過(guò)向后端請(qǐng)求相關(guān)數(shù)據(jù),并通過(guò)無(wú)刷新加載的方式進(jìn)行更新頁(yè)面(post請(qǐng)求)。那么數(shù)據(jù)進(jìn)行更新后,頁(yè)面上相應(yīng)的位置也能自動(dòng)做出對(duì)應(yīng)的修改,便是數(shù)據(jù)綁定。
在以前的開(kāi)發(fā)模式中,這一步一般通過(guò)jq操作DOM結(jié)構(gòu),從而進(jìn)行更新頁(yè)面。但這樣帶來(lái)的是大量的代碼和大量的操作。如果能在開(kāi)始的時(shí)候,便已經(jīng)確定好從后端獲取的數(shù)據(jù)到頁(yè)面上需要進(jìn)行的操作,當(dāng)數(shù)據(jù)發(fā)生改變,頁(yè)面的相關(guān)內(nèi)容也自動(dòng)發(fā)生變化,這樣便能極大地方便前端工程師的開(kāi)發(fā)。在新的框架中(angualr,react,vue等),通過(guò)對(duì)數(shù)據(jù)的監(jiān)視,發(fā)現(xiàn)變化便根據(jù)已經(jīng)寫(xiě)好的規(guī)則進(jìn)行修改頁(yè)面,便實(shí)現(xiàn)了數(shù)據(jù)綁定??梢钥闯?,數(shù)據(jù)綁定是M(model,數(shù)據(jù))通過(guò)VM(model-view,數(shù)據(jù)與頁(yè)面之間的變換規(guī)則)向V(view)的一個(gè)修改。
而雙向綁定則是增加了一條反向的路。在用戶(hù)操作頁(yè)面(比如在Input中輸入值)的時(shí)候,數(shù)據(jù)能及時(shí)發(fā)生變化,并且根據(jù)數(shù)據(jù)的變化,頁(yè)面的另一處也做出對(duì)應(yīng)的修改。有一個(gè)常見(jiàn)的例子就是淘寶中的購(gòu)物車(chē),在商品數(shù)量發(fā)生變化的時(shí)候,商品價(jià)格也能及時(shí)變化。這樣便實(shí)現(xiàn)了V——M——VM——V的一個(gè)雙向綁定。
AngularJs 為 scope 模型上設(shè)置了一個(gè) 監(jiān)聽(tīng)隊(duì)列,用來(lái)監(jiān)聽(tīng)數(shù)據(jù)變化并更新 view 。每次綁定一個(gè)東西到 view(html) 上時(shí) AngularJs 就會(huì)往 $watch 隊(duì)列里插入一條 $watch,用來(lái)檢測(cè)它監(jiān)視的 model 里是否有變化的東西。當(dāng)瀏覽器接收到可以被 angular context 處理的事件時(shí),$digest 循環(huán)就會(huì)觸發(fā)。$digest 會(huì)遍歷所有的 $watch。從而更新DOM。
$watch
這有點(diǎn)類(lèi)似于我們的觀察者模式,在當(dāng)前作用域$scope下,我們創(chuàng)建一個(gè)監(jiān)控器$watchers和一個(gè)監(jiān)聽(tīng)器$watch,$watchers 負(fù)責(zé)管理所有的 $watch,當(dāng)我們每次綁定到UI上的時(shí)候就<font color=red>自動(dòng)</font>創(chuàng)建一個(gè)$watch,并把它放到 $watchers。
controller.js
app.controller('MainCtrl', function($scope) {
$scope.Hello = "Hello";
$scope.world = "World";
});
index.html
<div>{{Hello}}</div>
這里,即便我們?cè)?scope上添加了兩個(gè)變量,<font color=red>但是只有一個(gè)綁定在了UI上,因此在這里只生成了一個(gè)$watch</font>
$digest
當(dāng)瀏覽器接收到可以被angular context處理的事件時(shí),$digest循環(huán)就會(huì)觸發(fā)。$digest將會(huì)遍歷我們的$watch,如果$watch沒(méi)有變化,這個(gè)循環(huán)檢測(cè)就將停止,如果有至少一個(gè)更新過(guò),這個(gè)循環(huán)就會(huì)再次觸發(fā),直到所有的$watch都沒(méi)有變化。這樣就能夠保證每個(gè)model都已經(jīng)不會(huì)再變化。這就是臟檢查(Dirty Checking)機(jī)制
controller.js
app.controller('MainCtrl', function() {
$scope.name = "Foo";
$scope.changeFoo = function() {
$scope.name = "Bar";
}
});
index.js
<div>{{ name }}</div>
<button ng-click="changeFoo()">Change the name</button>
- 當(dāng)我們按下按鈕
- 瀏覽器接收到一個(gè)事件,進(jìn)入angular context。
- $digest循環(huán)開(kāi)始執(zhí)行,查詢(xún)每個(gè)$watch是否變化。
- 由于監(jiān)視$scope.name的$watch報(bào)告了變化,它會(huì)強(qiáng)制再執(zhí)行一次$digest循環(huán)。
- 新的$digest循環(huán)沒(méi)有檢測(cè)到變化。
- 更新與$scope.name新值相應(yīng)部分的DOM。
$apply
$apply 我們可以直接理解為刷新UI。<font color=red>如果當(dāng)事件觸發(fā)時(shí),你調(diào)用$apply,它會(huì)進(jìn)入angular context,如果沒(méi)有調(diào)用就不會(huì)進(jìn)入,之后的$digest檢測(cè)機(jī)制就不會(huì)觸發(fā)</font>
app.directive('clickable', function() {
return {
restrict: "E",
scope: {
foo: '='
},
template: '<ul style="background-color: lightblue"><li>{{foo}}</li></ul>',
link: function(scope, element, attrs) {
element.bind('click', function() {
scope.foo++;
console.log(scope.foo);
});
}
}
});
當(dāng)我們調(diào)用clickable指令的時(shí)候,我們可以看到foo的值增加了,但是界面上顯示的內(nèi)容并沒(méi)有改變。$digest臟檢測(cè)機(jī)制沒(méi)有觸發(fā),檢測(cè)foo的$watch就沒(méi)有執(zhí)行。
$apply()方法的兩種形式
1) 無(wú)參
$scope.$apply();
element.bind('click', function() {
scope.foo++;
//if error
scope.$apply();
});
當(dāng)我們使用這種形式的時(shí)候,如果在scope.$apply之前程序發(fā)生異常,那scope.$apply沒(méi)有執(zhí)行,界面就不會(huì)更新
2) 有參
$scope.$apply(function(){
...
})
element.bind('click', function() {
scope.$apply(function() {
scope.foo++;
});
})
如果用這種形式,即使后面的發(fā)生異常,數(shù)據(jù)還是會(huì)更新。
在 AngularJS 中使用 $watch
常用的使用方式:
$scope.name = 'Hello';
$scope.$watch('name', function(newValue, oldValue) {
if (newValue === oldValue) { return; }
$scope.updated++;
});
傳入到$watch()中的第二個(gè)參數(shù)是一個(gè)回調(diào)函數(shù),該函數(shù)在name的值發(fā)生變化的時(shí)候會(huì)被調(diào)用。
如果要監(jiān)聽(tīng)的是一個(gè)對(duì)象,那還需要第三個(gè)參數(shù):
$scope.data.name = 'Hello';
$scope.$watch('data', function(newValue, oldValue) {
if (newValue === oldValue) { return; }
$scope.updated++;
}, true);
表示比較的是對(duì)象的值而不是引用,如果不加第三個(gè)參數(shù)true,在 data.name 變化時(shí),不會(huì)觸發(fā)相應(yīng)操作,因?yàn)橐玫氖峭灰谩?/p>
總結(jié)
1) 只有在$scope變量綁定到頁(yè)面上,才會(huì)創(chuàng)建 $watch
2) $apply決定事件是否可以進(jìn)入angular context
3) $digest 循環(huán)檢查model時(shí)最少兩次,最多10次(多于10次拋出異常,防止無(wú)限檢查)
4) AngularJs自帶的指令已經(jīng)實(shí)現(xiàn)了$apply,所以不需要我們額外的編寫(xiě)
5) 在自定義指令時(shí),建議使用帶function參數(shù)的$apply
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Angular4學(xué)習(xí)之Angular CLI的安裝與使用教程
網(wǎng)上很多教程過(guò)時(shí),命令在angular4中不適用等等,所以下面這篇文章主要給大家介紹了關(guān)于Angular4學(xué)習(xí)之Angular CLI的安裝與使用教程的相關(guān)資料,需要的朋友可以參考借鑒,下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2018-01-01
Angularjs實(shí)現(xiàn)控制器之間通信方式實(shí)例總結(jié)
這篇文章主要介紹了Angularjs實(shí)現(xiàn)控制器之間通信方式,結(jié)合實(shí)例形式總結(jié)分析了AngularJS控制器常用通信方式及相關(guān)操作注意事項(xiàng),需要的朋友可以參考下2018-03-03
AngularJS實(shí)現(xiàn)一次監(jiān)聽(tīng)多個(gè)值發(fā)生的變化
這文章給大家介紹了如何利用AngularJS一次監(jiān)聽(tīng)多個(gè)值發(fā)生的變化,文中通過(guò)示例代碼演示,這樣更方便大家理解學(xué)習(xí),有需要的可以參考借鑒。2016-08-08
Angular2學(xué)習(xí)筆記——詳解NgModule模塊
這篇文章主要介紹了Angular2學(xué)習(xí)筆記——詳解NgModule模塊,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-12-12
Angular6 用戶(hù)自定義標(biāo)簽開(kāi)發(fā)的實(shí)現(xiàn)方法
這篇文章主要介紹了Angular6 用戶(hù)自定義標(biāo)簽開(kāi)發(fā)的實(shí)現(xiàn)方法,下面我們就通過(guò)一個(gè)簡(jiǎn)單的例子演示Angular6中的這一新功能,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,需要的朋友可以參考下2019-01-01
Angular學(xué)習(xí)教程之RouterLink花式跳轉(zhuǎn)
這篇文章主要給大家介紹了關(guān)于Angular學(xué)習(xí)教程之RouterLink花式跳轉(zhuǎn)的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2018-05-05
Angular.js回顧ng-app和ng-model使用技巧
這篇文章主要回顧Angular.js中ng-app和ng-model使用技巧,感興趣的小伙伴們可以參考一下2016-04-04
AngularJS實(shí)踐之使用ng-repeat中$index的注意點(diǎn)
最近通過(guò)客戶(hù)的投訴主要到在ng-repeat中使用了$index引發(fā)的一個(gè)bug,下面一起來(lái)看看這個(gè)錯(cuò)誤是如何引發(fā)的, 以及如何避免這種bug產(chǎn)生,然后說(shuō)說(shuō)我們從中得到的經(jīng)驗(yàn)和教訓(xùn)。有需要的朋友們可以參考借鑒,下面來(lái)一起看看吧。2016-12-12
Angular 2父子組件數(shù)據(jù)傳遞之@Input和@Output詳解(下)
這篇文章主要給大家介紹了關(guān)于Angular 2父子組件數(shù)據(jù)傳遞之@Input和@Output的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起看看吧。2017-07-07

