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