引子
这篇文章是写给AngularJS新手的,如果你已经对AngularJS的双向数据绑定有了深入的了解,直接去阅读源代码好了。
背景
AngularJS开发者都想知道双向数据绑定是怎么实现的。与data-binding相关的术语琳琅满目: $watch,$apply,$digest,dirty-checking等等它们是如何工作的呢?让我们从头开始讲起吧
AngularJS 的双向数据绑定是被浏览器逼的
浏览器看上去很美,其实在数据交互这块儿,由于浏览器的“不作为”,导致浏览器的数据刷新成为一个难题。具体来说,浏览器可以很容易地监听一个事件,比如:用户点击一个按钮,或者在输入框里输入东西,为此还提供了事件回调函数的API,事件的回调函数就会在javascript解释器里执行;但反过来就没这么简单了,如果来自后台的数据发生了变化,需要通知给浏览器,让浏览器刷新,浏览器并没有提供这样的数据交互机制,对于开发者来说,这是一个难以逾越的障碍,怎么办呢? AngularJS出现了,它通过$scope 很好地实现了双向数据绑定,其背后的原理就是$watch,$apply,$digest,dirty-checking
$watch 队列($watch list)
从字面上看,watch 是观察的意思。 每次绑定一些东西到浏览器上时,就会往$watch队列里插入一条$watch。想象一下$watch就是那个可以检测它监视的model里时候有变化的东西。例如你有如下的代码
User: <input type="text" ng-model="user" /> Password: <input type="password" ng-model="pass" />
这里有个$scope.user,它被绑定在了第一个输入框上,还有个$scope.pass,它被绑定在了第二个输入框上;然后在$watch list里面加入两个$watch:
创建一个 controllers.js 文件,代码如下:
app.controller('MainCtrl', function($scope) { $scope.foo = "Foo"; $scope.world = "World"; });
对应的html 文件, index.html 代码如下:
Hello, {{ World }}
这里,即便在$scope上添加了两个东西,但是只有一个绑定在了UI上,因此只生成了一个$watch. 再看下面的例子:
controllers.js
app.controller('MainCtrl', function($scope) { $scope.people = [...]; });
对应的html文件 index.html
<ul> <li ng-repeat="person in people"> {{person.name}} - {{person.age}} </li> </ul>
这样看来,又生成了多个$watch。每个person有两个(一个name,一个age),然后ng-repeat是一个循环,因此10个person一共是(2 * 10) +1,也就是说有21个$watch。 因此,每一个绑定到了浏览器上的数据都会生成一个$watch。对,那这写$watch是什么时候生成的呢? 先回顾下AngularJS的加载原理