首頁  >  文章  >  web前端  >  了解angularjs中的髒檢查

了解angularjs中的髒檢查

青灯夜游
青灯夜游轉載
2021-01-20 17:26:301853瀏覽

了解angularjs中的髒檢查

相關推薦:《angularjs教學

angularjs實作了雙向綁定,與vue的defineProperty不同,它的原理在於它的髒檢查機制,以下做了一些總結;

angular.js介紹

  • 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重新渲染到介面

$apply觸發$digest

    一般不呼叫$digest, 呼叫$apply,它內部會觸發$digest遞歸遍歷
  • angular的內部指令封裝了$apply,例如ng-click,所以一般不用手動呼叫apply
手動呼叫apply

    部分時候需要手動觸發
  • function($timeout) {
      // 当我们通过on('click')的方式触发某些更新的时候,可以这样做
      $timeout(() => { // 内置语法糖 $http, $timeout已经包含了apply
        $scope.name = 'lily'
      })
      // 也可以这样做
      $element.on('click', () => {
        $scope.name = 'david'
        $scope.$apply()
      })
    }
注意:在遞歸過程中,絕對不能手動呼叫$apply,例如在ng-click的函數中,例如在$watch的回呼函數中。
最後,實作一個簡易髒檢查機制

<!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(&#39;$digest&#39;);
      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(&#39;[ng-bind]&#39;);
      console.log(&#39;list&#39;, list)
      for (let i = 0, l = list.length; i < l; i++) {
        let bindData = list[i].getAttribute(&#39;ng-bind&#39;);
        console.log(&#39;bindData&#39;, bindData)
        console.log(&#39;list[i]&#39;, 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(&#39;data&#39;, function(newValue, oldValue) { // 监听
        console.log("new: " + newValue + "=========" + "old: " + oldValue);
    });


    // 手动为button按钮添加onclick事件,并为通过闭包为其绑定了全局scope对象,绑定apply方法
    // 类似angular内部实现
    function startBind() {
      let list = document.querySelectorAll(&#39;[ng-click]&#39;);
      for (let i = 0, l = list.length; i < l; i++) {
        list[i].onclick = (function (index) {
          return function () {
            let func = this.getAttribute(&#39;ng-click&#39;);
            $scope[func]($scope);
            $scope.$digest();
            $scope.$apply()
          }
        })(i)
      }
    }

    // 初始化
    startBind();
  }

</script>

</html>

更多程式相關知識,請造訪:

程式設計影片! !

以上是了解angularjs中的髒檢查的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:cnblogs.com。如有侵權,請聯絡admin@php.cn刪除