溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

angularjs中臟檢查的示例分析

發(fā)布時(shí)間:2021-01-21 09:41:48 來源:億速云 閱讀:367 作者:小新 欄目:web開發(fā)

小編給大家分享一下angularjs中臟檢查的示例分析,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!

angularjs實(shí)現(xiàn)了雙向綁定,與vue的defineProperty不同,它的原理在于它的臟檢查機(jī)制,以下做了一些總結(jié);

angular.js介紹

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

$apply觸發(fā)$digest
  • 一般不調(diào)用$digest, 調(diào)用$apply,它內(nèi)部會(huì)觸發(fā)$digest遞歸遍歷

  • angular的內(nèi)部指令封裝了$apply,比如ng-click,所以一般不用手動(dòng)調(diào)用apply

手動(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()
  })
}
注意:在遞歸過程中,絕對(duì)不能手動(dòng)調(diào)用$apply,比如在ng-click的函數(shù)中,比如在$watch的回調(diào)函數(shù)中。

最后,實(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è)資訊頻道!

向AI問一下細(xì)節(jié)

免責(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)容。

AI