脏检查是AngularJS的核心机制之一,它是实现双向绑定、MVVM模式的重要基础。
一、digest循环
AngularJS将双向绑定转换为一个堆watch表达式,然后递归检查这些watch表达式的结果有没有变化,如果变了,就会执行响应的watcher函数,等到model值不再变化,也就不会再有watcher函数被触发。
此时,浏览器会重新渲染DOM来体现model的改变,这里所说的watcher函数,是有view上的指令,如ngBind、ngShow、ngHide,或{{}}表达式所注册,它其实是指令在AngularJS的complie阶段会逐一解析、注册。
AngularJS并不是周期性触发脏检查。只有当view中事件,ajax请求或者 timeout 延迟事件,才会触发脏检查。$scope.$apply是触发脏检查机制的公开接口。我们在封装第三方jQuery插件时,不能自动更新View,需要手动调用$scope.$apply。
二、$watch 对象
Angular 每一个绑定到view上的数据,就会有一个 $watch 对象,每当我们将数据绑定到 view上,AngularJS 就会向你的 watchList 上插入一个 $watch对象。这个对象包含三个参数
watch = { name:'', //当前的watch 对象 观测的数据名 getNewValue:function($scope){ //得到新值 ... return newValue; }, listener:function(newValue,oldValue){ // 当数据发生改变时需要执行的操作 ... } }
getNewValue() 可以得到当前$scope 上的最新值,listener 函数得到新值和旧值并进行一些操作。而常常我们在使用AngularJS的时候,listener 一般都为空,只有当我们需要监测更改事件的时候,才会显示地添加监听。
三、双向数据绑定
AngularJS实现了双向数据绑定,就是view中的操作能实时反应到viewModel数据,viewModel数据的更改也能在View呈现。view到viewModel数据的更改,是由 vew中绑定的事件,ajax请求,或者tmeout 等回调操作完成,而viewModel数据到view的更新呈现则是由脏检查来做。只有当触发view中的事件,ajax请求或者 timeout 延迟,才会触发脏检查,如用户在文本框里输入了数据,或者ajax取回的新的数据要应用在程序中,或者用户点击了东西需要我们更改一些数据。如果直接使用DOM的onclick方法,数据虽然变更了,但是还没有往View上填充,我们需要在此做一些附加操作。
双向绑定机制,在DOM操作中,虽然更新了数据的值,但是并没有立即反映到View上,而是通过 apply() 来反映到view上。AngularJS的ng-click 封装了click,然后调用一次 apply 函数,把数据呈现到界面上。在AngularJS 的apply函数中,先进行脏检测,检测 oldValue 和 newVlue 是否相等,如果不相等,将newValue 反馈到界面上,如果通过 $watch 注册了 listener事件,那么就会调用该事件。
四、过程描述
当接收View上的指令所转发的事件时,就会切换到AngularJS的上下文环境来响应这类事件,digest循环就会触发;遍历一遍所有watcher函数(表达式或对象)称为一轮脏检查,执行完一轮检查,若任一watcher所监听的值改变过,那就重新再进行一次脏检查,知道所有watcher所监听的值都没有变化。
从第一轮脏检查到结果变得稳定,这是一次digest循环完整过程,当循环结束后,才把变化更新到DOM去,这样可以合并多个更新,防止频繁的DOM操作。