Angular双向数据绑定原理之脏检查分析

前端之家收集整理的这篇文章主要介绍了Angular双向数据绑定原理之脏检查分析前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

angular双向数据绑定原理

  • 从UI到数据:UI事件,ajax请求,timeout等。
  • 从数据到UI:脏检查

脏检查

1. 添加监听器$watcher

为scope中渲染在页面中的每个数据添加监听器$watcher,当添加$watcher的数据发生变化时,调用所有watcher对应的listener,执行数据变化后的操作。

function $scope() {
    this.$$watchList = [];
}

$scope.prototype.$watch = function (name,getNewValue,listener) {
    var watch = {
        name: name,//scope中的数据名称
        getNewValue: getNewValue,//数据的当前值
        listener: listener || function () {} //数据变化时执行的操作
    };

    this.$$watchList.push(watch);
};

2. 数据改变时调用所有watcher中的listener

$digest里面调用所有watcher

$scope.prototype.$digest = function () {
    var list = this.$$watchList;
    for (var i = 0,l = list.length; i < l; i++) {
        var watch = list[i];
        var newValue = watch.getNewValue();
        var oldValue = watch.last;//第一次执行时oldValue为undefined
        if (newValue != oldValue) {
            watch.listener(newValue,oldValue);
        }
        watch.last = newValue;
    }
};

此时当newValue != oldValue时便执行$watcher对应的listener.
测试:

var scope = new $scope();
scope.hello = 5;
scope.$watch('hello',function () {
        return scope.hello;
    },function (newValue,oldValue) {
      console.log('newValue: ' + newValue + ';oldValue:' + oldValue);
    });
scope.$digest();
scope.hello = 10;
scope.$digest();
scope.hello = 20;
scope.$digest();
scope.hello = 20;
scope.$digest();
scope.hello = 26;
scope.$digest();

newValue: 5;oldValue:undefined
newValue: 10;oldValue:5
newValue: 20;oldValue:10
newValue: 26;oldValue:20

3. 持续监听

然而当在另一个watcher中改变其他watcher中的值时,上面的digest不能监听到变化:

var scope = new $scope();
scope.hello = 5;
scope.hello2 = 15;
scope.$watch('hello',oldValue) {
        console.log('newValue: ' + newValue + ';oldValue:' + oldValue);
});
scope.$watch('hello2',function () {
        return scope.hello2;
    },oldValue) {
        scope.hello = 10;//这里的改变并未检测到
        console.log('newValue: ' + newValue + ';oldValue:' + oldValue);
});
scope.$digest();

newValue: 5;oldValue:undefined
newValue: 15;oldValue:undefined

因此,在scope中值发生变化时需要持续进行digest,在digest中设置所有数据是否为脏的标志dirty,即每次执行digest,若有数据发生变化,就将dirty置为true,若dirtytrue继续执行digest

$scope.prototype.$$digestOnce = function () {
    var dirty;
    var list = this.$$watchList;

    for (var i = 0,l = list.length; i < l; i++) {
        var watch = list[i];
        var newValue = watch.getNewValue();
        var oldValue = watch.last;
        if (newValue !== oldValue) {
            watch.listener(newValue,oldValue);
            // 因为listener操作,已经检查过的数据可能变脏
            dirty = true;
        }
        watch.last = newValue;
    }
    return dirty;
};
$scope.prototype.$digest = function () {
    var dirty = true;
    while (dirty) {
        dirty = this.$$digestOnce();
    }
};
var scope = new $scope();
scope.hello1 = 5;
scope.hello2 = 10;
scope.$watch('hello1',function () {
        return scope.hello1;
    },oldValue) {
        console.log('newValue:' + newValue + '~~~~' + 'oldValue:' + oldValue);
    });
scope.$watch('hello2',oldValue) {
        scope.hello1 = 20;//这里的值被监听到
        console.log('newValue:' + newValue + '~~~~' + 'oldValue:' + oldValue);
    });
scope.$digest();

newValue:5~~~~oldValue:undefined
newValue:10~~~~oldValue:undefined
newValue:20~~~~oldValue:5

4. 防止持续运行digest陷入死循环

然而当在watcher中互相改变对方的值,则会无限次digest,此时陷入死循环,因此在,digest中设置最多执行次数

$scope.prototype.$digest = function() {
    var dirty = true;
    var checkTimes = 0;
    var maxTime = 10;
    while(dirty) {
        dirty = this.$$digestOnce();
        checkTimes++;
        if(checkTimes > maxTime && dirty){
            console.log("数据持续脏");
            throw new Error("检测超过10次");
        }
    }
};
原文链接:https://www.f2er.com/angularjs/146484.html

猜你在找的Angularjs相关文章