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

来源:互联网 发布:2017淘宝企业开店流程 编辑:程序博客网 时间:2024/04/29 19:24

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:undefinednewValue: 10;oldValue:5newValue: 20;oldValue:10newValue: 26;oldValue:20

3. 持续监听

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

var scope = new $scope();scope.hello = 5;scope.hello2 = 15;scope.$watch('hello',    function () {        return scope.hello;    },    function (newValue, oldValue) {        console.log('newValue: ' + newValue + ';oldValue:' + oldValue);});scope.$watch('hello2',    function () {        return scope.hello2;    },    function (newValue, oldValue) {        scope.hello = 10;//这里的改变并未检测到        console.log('newValue: ' + newValue + ';oldValue:' + oldValue);});scope.$digest();newValue: 5;oldValue:undefinednewValue: 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;    },    function (newValue, oldValue) {        console.log('newValue:' + newValue + '~~~~' + 'oldValue:' + oldValue);    });scope.$watch('hello2',    function () {        return scope.hello2;    },    function (newValue, oldValue) {        scope.hello1 = 20;//这里的值被监听到        console.log('newValue:' + newValue + '~~~~' + 'oldValue:' + oldValue);    });scope.$digest();newValue:5~~~~oldValue:undefinednewValue:10~~~~oldValue:undefinednewValue: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次");        }    }};