您好,登錄后才能下訂單哦!
小編給大家分享一下angularjs中臟檢查的示例分析,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!
angularjs實(shí)現(xiàn)了雙向綁定,與vue的defineProperty不同,它的原理在于它的臟檢查機(jī)制,以下做了一些總結(jié);
AngularJs是mvvm框架,它的組件是vm組件,scope是vm組件的數(shù)據(jù)集合
AngularJs通過directive來聲明vm的行為,它實(shí)現(xiàn)為一個(gè)watcher,監(jiān)聽scope的屬性的變化,把最新的屬性更新UI
AngularJs的雙向綁定:如:一個(gè)是將$scope屬性值綁定到HTML結(jié)構(gòu)中,當(dāng)$scope屬性值發(fā)生變化的時(shí)候界面也發(fā)生變化;另一個(gè)是,當(dāng)用戶在界面上進(jìn)行操作,例如點(diǎn)擊、輸入、選擇時(shí),自動(dòng)觸發(fā)$scope屬性的變化(界面也可能跟著變)
監(jiān)聽scope的屬性變更:臟檢查(dirty check )
angular根本不監(jiān)聽數(shù)據(jù)的變動(dòng),而是在恰當(dāng)?shù)臅r(shí)機(jī)($watch)從$rootScope開始遍歷所有$scope,
檢查它們上面的屬性值是否有變化,如果有變化,就用一個(gè)變量dirty記錄為true,再次進(jìn)行遍歷($digest),
如此往復(fù),直到某一個(gè)遍歷完成時(shí),這些$scope的屬性值都沒有變化時(shí),結(jié)束遍歷。
由于使用了一個(gè)dirty變量作為記錄,因此被稱為臟檢查機(jī)制。
簡而言之: 當(dāng)作用域創(chuàng)建時(shí),angular會(huì)去解析模板,將綁定值和事件調(diào)用找出來并用$watch綁定;
$scope.$watch(string|function, listener, objectEquality, prettyPrintExpression) // string: 驗(yàn)證值或者function執(zhí)行后return的string // listener: 如果驗(yàn)證值不同,則執(zhí)行該監(jiān)聽函數(shù) // objectEquality:執(zhí)行深檢查
完成綁定后,就會(huì)自動(dòng)檢測這些屬性的變化,執(zhí)行$watch, 那么對(duì)應(yīng)的信息被綁定到angular內(nèi)部的一個(gè)$$watchers中,
它是一個(gè)隊(duì)列(數(shù)組),而當(dāng)$digest被觸發(fā)時(shí),angular就會(huì)去遍歷這個(gè)數(shù)組,
并且用一個(gè)dirty變量記錄$$watchers里面記錄的那些$scope屬性是否有變化
接下來的流程:
判斷dirty是否為true,如果為false,則不進(jìn)行$digest遞歸。(dirty默認(rèn)為true)
遍歷$$watchers,取出對(duì)應(yīng)的屬性值的老值和新值根據(jù)objectEquality進(jìn)行新老值的對(duì)比。
如果兩個(gè)值不同,則繼續(xù)往下執(zhí)行。如果兩個(gè)值相同,則設(shè)置dirty為false。
檢查完所有的watcher之后,如果dirty還為true, 設(shè)置dirty為true用新值代替老值;
這樣,在下一輪遞歸的時(shí)候,老值就是這一輪的新值再次調(diào)用$digest(簡單說就是執(zhí)行兩次遞歸遍歷檢查新舊值變化)
將變化后的$scope重新渲染到界面
一般不調(diào)用$digest, 調(diào)用$apply,它內(nèi)部會(huì)觸發(fā)$digest遞歸遍歷
angular的內(nèi)部指令封裝了$apply,比如ng-click,所以一般不用手動(dòng)調(diào)用apply
部分時(shí)候需要手動(dòng)觸發(fā)
function($timeout) { // 當(dāng)我們通過on('click')的方式觸發(fā)某些更新的時(shí)候,可以這樣做 $timeout(() => { // 內(nèi)置語法糖 $http, $timeout已經(jīng)包含了apply $scope.name = 'lily' }) // 也可以這樣做 $element.on('click', () => { $scope.name = 'david' $scope.$apply() }) }
最后,實(shí)現(xiàn)一個(gè)簡易臟檢查機(jī)制
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>angularjs臟檢查實(shí)現(xiàn)</title> </head> <style type="text/css"> button { height: 60px; width: 100px; } p { margin-left: 20px; } </style> <body> <div> <button type="button" ng-click="increase">增加</button> <button type="button" ng-click="decrease">減少</button> 數(shù)量:<span ng-bind="data">0</span> </div> <br> </body> <script> window.onload = function () { /** * 聲明構(gòu)造函數(shù) */ function Scope() { this.$$watchList = []; // angular內(nèi)部會(huì)聲明一個(gè)數(shù)組(包含監(jiān)聽的對(duì)象),在digest執(zhí)行時(shí)去遍歷 } /** * 屬性賦值給$scope * 類似angular解析controller的模板,把模板中的屬性解析出來,屬性賦值給$scope */ Scope.prototype.getNewValue = function () { return $scope[this.name]; } /** * 實(shí)現(xiàn)監(jiān)聽 */ Scope.prototype.$watch = function (name, listener) { let watch = { name: name, getNewValue: this.getNewValue, listener: listener || function () { } }; // 當(dāng)作用域創(chuàng)建時(shí),angular會(huì)去解析模板,$watch用來綁定監(jiān)聽值和監(jiān)聽函數(shù) this.$$watchList.push(watch); } /** * 檢查dirty,循環(huán)更新scope上的最新值 */ Scope.prototype.$digest = function () { console.log('$digest'); let dirty = true; // 默認(rèn)dirty變量為true let checkTimes = 0; while (dirty) { dirty = this.$valScope(); checkTimes++; if (checkTimes > 10 && dirty) { throw new Error("循環(huán)過多"); } } } /** * 驗(yàn)證值是否有變化 */ Scope.prototype.$valScope = function () { let dirty; let list = this.$$watchList; for (let i = 0; i < list.length; i++) { let watch = list[i]; let newValue = watch.getNewValue(); let oldValue = watch.last || undefined; if (newValue !== oldValue) { watch.listener(newValue, oldValue); dirty = true; // 如果新舊值不同,則繼續(xù)遍歷 } else { dirty = false; } watch.last = newValue; } return dirty; } /** * 刷新scope */ Scope.prototype.$apply = function (params) { let list = document.querySelectorAll('[ng-bind]'); console.log('list', list) for (let i = 0, l = list.length; i < l; i++) { let bindData = list[i].getAttribute('ng-bind'); console.log('bindData', bindData) console.log('list[i]', list[i]) list[i].innerHTML = $scope[bindData]; } } let $scope = new Scope(); // 實(shí)例化,聲明$scope對(duì)象集合 $scope.data = 0; $scope.increase = function () { this.data++; }; $scope.decrease = function () { this.data--; }; $scope.$watch('data', function(newValue, oldValue) { // 監(jiān)聽 console.log("new: " + newValue + "=========" + "old: " + oldValue); }); // 手動(dòng)為button按鈕添加onclick事件,并為通過閉包為其綁定了全局scope對(duì)象,綁定apply方法 // 類似angular內(nèi)部實(shí)現(xiàn) function startBind() { let list = document.querySelectorAll('[ng-click]'); for (let i = 0, l = list.length; i < l; i++) { list[i].onclick = (function (index) { return function () { let func = this.getAttribute('ng-click'); $scope[func]($scope); $scope.$digest(); $scope.$apply() } })(i) } } // 初始化 startBind(); } </script> </html>
以上是“angularjs中臟檢查的示例分析”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對(duì)大家有所幫助,如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。