>  기사  >  웹 프론트엔드  >  Anglejs의 더티 검사 이해하기

Anglejs의 더티 검사 이해하기

青灯夜游
青灯夜游앞으로
2021-01-20 17:26:301945검색

Anglejs의 더티 검사 이해하기

관련 권장사항: "angularjs 튜토리얼"

angularjs는 vue의 DefineProperty와는 다른 원칙을 가지고 있습니다. 여기에는 몇 가지 요약이 있습니다.

AngularJs는 vm 구성 요소이고 범위는 vm 구성 요소의 데이터 모음입니다.

    AngularJs는 vm의 동작을 선언하기 위해 지시문을 사용하여 속성의 변경 사항을 수신합니다. 범위 및 최신 속성 업데이트 UI
  • AngularJs의 양방향 바인딩: 예: $scope 속성 값이 변경되면 인터페이스도 변경됩니다. 다른 하나는 $scope 속성 값을 인터페이스에 바인딩하는 것입니다. 클릭, 입력 및 선택과 같은 작업은 $scope 속성의 변경 사항을 자동으로 트리거합니다. 그에 따라 인터페이스도 변경될 수 있음)
  • 범위 속성 변경 모니터링: dirty check
  • Dirty check

angular는 모든 데이터 변경 사항을 모니터링하지 않지만 적절한 시간($watch)에 $rootScope에서 시작하여 모든 $scope를 탐색합니다. ,

    해당 속성 값이 변경되었는지 확인하고, 변경 사항이 있는 경우 더티 변수를 사용하여 이를 true로 기록하고 Traverse($digest),
  • 등을 다시 수행합니다. 특정 순회가 완료되고 이러한 $scope의 속성 값이 변경되지 않은 경우 순회가 종료됩니다.
  • 더티 변수가 레코드로 사용되기 때문에 더티 검사 메커니즘이라고 합니다.
  • 간단히 말하면, 범위가 생성되면 각도는 템플릿을 구문 분석하고 바인딩 값과 이벤트 호출을 찾아 $watch로 바인딩합니다.
$scope.$watch(string|function, listener, objectEquality, prettyPrintExpression)
// string: 验证值或者function执行后return的string
// listener: 如果验证值不同,则执行该监听函数
// objectEquality:执行深检查

바인딩을 완료한 후 이러한 속성이 변경되면 $watch가 실행되고 해당 정보가 각도 내부의 $$watchers에 바인딩됩니다.
    큐(배열)이며 $digest가 트리거되면 각도가 이 배열을 통과합니다.
  • 더티 변수를 사용하여 $$watchers에 기록된 $scope 속성이 변경되었는지 기록합니다.
  • 다음 프로세스:

더티가 참인지 확인하고, 거짓이면 $digest가 재귀를 수행하지 않습니다. . (Dirty의 기본값은 true)
  • Traverse $$watchers, 해당 속성 값의 이전 값과 새 값을 꺼내고 objectEquality를 기반으로 이전 값과 새 값을 비교합니다.
  • 두 값이 다르면 계속 실행하세요. 두 값이 동일한 경우 dirty를 false로 설정합니다.
  • 모든 감시자를 확인한 후에도 더티가 여전히 true이면 더티를 true로 설정하고 이전 값을 새 값으로 바꿉니다.
  • 이렇게 하면 다음 재귀 라운드에서 이전 값이 됩니다. 이 라운드의 새 값 $digest를 다시 호출합니다(간단히 말하면 두 번의 재귀 순회를 수행하여 이전 값과 새 값의 변경 사항을 확인합니다)
  • 변경된 $scope를 인터페이스에 다시 렌더링
  • $apply 트리거 $digest

일반적으로 $digest는 호출되지 않습니다. $apply를 호출하면 내부적으로 $digest 재귀 탐색이 실행됩니다.

    angular의 내부 지침은 ng-click과 같은 $apply를 캡슐화하므로 일반적으로 수동으로 수행할 필요가 없습니다. call apply
  • 수동으로 apply 호출

수동으로 실행해야 하는 경우도 있습니다

    function($timeout) {
      // 当我们通过on('click')的方式触发某些更新的时候,可以这样做
      $timeout(() => { // 内置语法糖 $http, $timeout已经包含了apply
        $scope.name = 'lily'
      })
      // 也可以这样做
      $element.on('click', () => {
        $scope.name = 'david'
        $scope.$apply()
      })
    }
  • 참고: 재귀 프로세스 중에 ng-click 함수에서와 같이 $apply를 수동으로 호출하면 안 됩니다. $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>
더 많은 프로그래밍 관련 지식을 보려면

프로그래밍 비디오

를 방문하세요! !

위 내용은 Anglejs의 더티 검사 이해하기의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 cnblogs.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제