Home  >  Article  >  Web Front-end  >  Understanding dirty checking in angularjs

Understanding dirty checking in angularjs

青灯夜游
青灯夜游forward
2021-01-20 17:26:301954browse

Understanding dirty checking in angularjs

Related recommendations: "angularjs Tutorial"

angularjs implements two-way binding, which is different from vue's defineProperty. Its principle lies in its The following is a summary of the dirty checking mechanism;

Introduction to angular.js

  • AngularJs is an mvvm framework, its component is the vm component, and scope is the data collection of the vm component
  • AngularJs declares the behavior of the vm through directive, which is implemented as a watcher, listens for changes in the properties of the scope, and updates the UI with the latest properties
  • Two-way binding of AngularJs: For example: one is to $ The scope attribute value is bound to the HTML structure. When the $scope attribute value changes, the interface also changes. The other is that when the user performs operations on the interface, such as clicking, inputting, and selecting, the $scope attribute is automatically triggered. Changes (the interface may also change)
  • Listen to scope attribute changes: dirty check

Dirty check

  • angular does not listen at all Data changes, but at the right time ($watch), traverse all $scopes starting from $rootScope,
  • Check whether the attribute values ​​​​on them have changed, and if there are changes, use a variable dirty to record it as true, traverse ($digest) again,
  • and so on, until a certain traversal is completed and the attribute values ​​​​of these $scopes have not changed, the traversal ends.
  • Because a dirty variable is used as a record, it is called a dirty check mechanism.

In short: When the scope is created, angular will parse the template, find out the binding values ​​​​and event calls and bind them with $watch;

$scope.$watch(string|function, listener, objectEquality, prettyPrintExpression)
// string: 验证值或者function执行后return的string
// listener: 如果验证值不同,则执行该监听函数
// objectEquality:执行深检查
  • After completing the binding, it will automatically detect changes in these properties and execute $watch. Then the corresponding information will be bound to a $$watchers inside angular.
  • It is A queue (array), and when $digest is triggered, angular will traverse the array,
  • and use a dirty variable to record whether the $scope attributes recorded in $$watchers have changed

Next process:

  • Determine whether dirty is true. If it is false, $digest recursion will not be performed. (dirty defaults to true)

  • Traverse $$watchers, take out the old value and new value of the corresponding attribute value, and compare the old and new values ​​according to objectEquality.

  • If the two values ​​are different, continue execution. If the two values ​​are the same, set dirty to false.

  • After checking all watchers, if dirty is still true, set dirty to true and replace the old value with the new value;

  • In this way, In the next round of recursion, the old value is the new value of this round and call $digest again (simply speaking, it is to perform two recursive traversals to check the changes of the old and new values)

  • The changed value $scope re-renders to the interface

$apply triggers $digest

  • Generally, $digest is not called. Calling $apply will internally trigger $digest recursive traversal.
  • angular’s ​​internal instructions encapsulate $apply, such as ng-click, so there is generally no need to manually call apply

Manually calling apply

  • is sometimes required Manual trigger
function($timeout) {
  // 当我们通过on('click')的方式触发某些更新的时候,可以这样做
  $timeout(() => { // 内置语法糖 $http, $timeout已经包含了apply
    $scope.name = 'lily'
  })
  // 也可以这样做
  $element.on('click', () => {
    $scope.name = 'david'
    $scope.$apply()
  })
}
Note: During the recursive process, $apply must not be called manually, such as in the ng-click function, such as in the callback function of $watch.

Finally, implement a simple dirty checking mechanism

<!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>

For more programming-related knowledge, please visit: Programming Video! !

The above is the detailed content of Understanding dirty checking in angularjs. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:cnblogs.com. If there is any infringement, please contact admin@php.cn delete