angularjs使用者想要知道数据绑定(data-binding)是如何工作的,就会遇到很多的关的术语,比如$wacth,$apply,$digest,dirty-checking(脏值检测)...等等,这些又是做什么的呢?
先介绍一下重要背景~三个概念~
- Dirty Checking – AngularJS内部比较value现在的值和之前的值,如果发生了改变,就触发change事件。
- Digest – 执行Dirty Checking的机制,由$digest()触发。
- Apply – 当dom事件在AngularJS机制外被触发时,需要通知AngularJS进行Digest。由$apply触发。
1.每当你在ui上绑定了东西(比如使用ng-model, {{name}}),就会添加了一个$wacth到$watch list中,你可以把$watch想象成为一个能够察觉model的变化的检测器;当我们在ui上使用(绑定)用directives 指令的时候,就创建了一个$watch,那它是什么时候创建的呢? 当我们的模板加载完成(亦叫做linking phase),compiler就会找到所有的指令,并创建对应的$watch。
2.当浏览器发送一个事件,我们就能通过angular context管理这个事件,此时$digest就会被激活 ( 除了ng-click之外,还有其他一些内置的指令/服务可以让你更改模型(例如ng-model,$timeout 等),并自动触发一个 $digest循环。)
这个事件轮询loop由两个小loop组成,
一个是处理$evalAsync队列
一个是处理$watch list,这就是本文的主题了。
那处理的过程是怎样的?$digest会轮询我们有的$watch list 看数据是否改变,大概就像下面这样
---》Hey,$watch,你的value值是多少啊?
---》我的value值是9。
---》好的,有变化吗?
---》没有。
(什么都没有变化,就会跳到下个继续询问)
---》来,你的value值是多少啊?
---》是foo。
---》有变化吗?
---》有啊,本来是bar的
(很好,那么现在我们有一个DOM要更新下了)
流程就会这样继续下去,直到$watch list中所有的$watch都被询问...
当$degest loop完成,DOM就会发生改变
现在讲下脏值检测(dirty-checking),现在所有的$watch都被轮询过了,
会再次询问是否所有的$watch都已经更新了?
如果其中有一个改变了,就会重新轮询,直到所有的$wacth没有改变。这样做事为了确保所有的model都是“干净的”
注意:如果轮询loop超过十次,就会抛出异常,来退出无限的轮询。
angular使用dirty(脏值)检查机制来实现了双向绑定,大体思路为:通过watch一遍又一遍地监听脏值也就是所说的脏值检查,当某个dirty由false变为true时,触发view重新渲染。
通过$digest的理解,我们知道是存在DOM事件,XHR响应事件,Location变更,定时器$timeout, $interval的情况下angular认为需要检查数据有无变化时才会调用$apply进而调用了$digest遍历监听。当一些变量被改变时并不在上述几种情况下,如使用原生的setTimeout()而非$timeout时,因为angular没有自动调用$apply所以我们需要手动调用以下$apply。
最后,我希望你看完就明白了 Angular中data-binding的工作原理,我猜你看完的第一印象会觉得dirty-checking是很慢的
实际上它是很快的,实际上只有页面上达到2000-3000 个$watch的时候,它才会出现性能上的延迟。一般的应用应该不会有这么复杂,否则就要思考一下是否设计不合理了。