十年网站开发经验 + 多家企业客户 + 靠谱的建站团队
量身定制 + 运营维护+专业推广+无忧售后,网站问题一站解决
小编给大家分享一下AngularJS双向数据绑定原理之$watch、$apply和$digest怎么用,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!
成都创新互联公司主要从事网站设计制作、成都做网站、网页设计、企业做网站、公司建网站等业务。立足成都服务子洲,10多年网站建设经验,价格优惠、服务专业,欢迎来电咨询建站服务:18982081108
背景
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: Password:
这里有个$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
这样看来,又生成了多个$watch。每个person有两个(一个name,一个age),然后ng-repeat是一个循环,因此10个person一共是(2 * 10) +1,也就是说有21个$watch。 因此,每一个绑定到了浏览器上的数据都会生成一个$watch。对,那这写$watch是什么时候生成的呢? 先回顾下AngularJS的加载原理
AngularJS的加载原理:
AngularJS的模板加载分为编译(compile)和链接(linking)两个阶段,在linking阶段,AngularJS解释器会寻找每个directive,然后生成每个需要的$watch。对了,$watch就是在这个阶段生成的。
接下来,开始用到 $digest了
$digest 循环
从字面上看,digest是 “消化”的意思,总感觉这个名字怪怪的,跟不可思议的是 dirty-checking, 字面意思“脏检查”,还是不翻译为好。原作者的本意肯定不是这个意思,只可意会不可言传!
$digest 是一个循环,它在循环做什么呢? $digest 在遍历我们的$watch。 $digest 一个个地询问$watch —— “嗨,你观察的数据发生变化了没?”
这个遍历就是所谓的dirty-checking。既然所有的$watch都检查完了,那就要问了:有没有$watch更新过?如果有至少一个更新过,这个循环就会再次触发,直到所有的$watch都没有变化。这样就能够保证每个model都已经不会再变化。记住如果循环超过10次的话,它将会抛出一个异常,以免出现无限循环。 当$digest循环结束时,DOM相应地变化。
看段代码,例如: controllers.js
app.controller('MainCtrl', function() { $scope.name = "Foo"; $scope.changeFoo = function() { $scope.name = "Bar"; } });
对应的html文件,index.html
{{ name }}
这里只有一个$watch,因为ng-click不生成$watch(函数是不会变的)。
$digest 执行的流程是:
在浏览器按下按钮;
浏览器接收到一个事件,进入angular context。
$digest循环开始执行,查询每个$watch是否变化。
由于监视$scope.name的$watch报告了变化,它会强制再执行一次$digest循环。
新的$digest循环没有检测到变化,此时浏览器拿回控制权,更新与$scope.name新值相应部分的DOM。
从中可以看出AngularJS的一个明显的不足:每一个进入angular context的事件都会执行一个$digest循环,哪怕仅仅是输入一个字母,$digest 都会遍历整个页面的所有$watch。
$apply 的应用
Angular context 是整个Angular的上下文,也可以把它理解为Angular容器,那么,是谁来决定哪些事件可以进入 Angular Context,哪些事件又不能进入呢? 其控制器在 $apply手上。
如果当事件触发时,调用$apply,它会进入angular context,如果没有调用就不会进入。你可能会问:刚才的例子并没有调用$apply,这是怎么回事呢?原来,是Angular背后替你做了。当点击带有ng-click的元素时,事件就会被封装到一个$apply调用中。如果有一个ng-model="foo"的输入框,当输入一个字母 f 时,事件就会这样调用,$apply("foo = 'f';")。
$apply的应用场景
$apply是$scope的一个函数,调用它会强制一次$digest循环。如果当前正在执行$apply循环,则会抛出一个异常。
如果浏览器上数据没有及时刷新,可以通过调用$scope.$apply() 方法,强行刷新一遍。
通过 $watch 监控自己的$scope
test Name updated: {{updated}} times.
代码说明:
当controller 执行到 $watch时,它会立即调用一次,所以把updated的值设为 -1 。 上输入框中输入字符发生变化时,你会看到 updated 的值随之变化,而且能显示变化的次数。
$watch 检测到的数据变化
看完了这篇文章,相信你对“AngularJS双向数据绑定原理之$watch、$apply和$digest怎么用”有了一定的了解,如果想了解更多相关知识,欢迎关注创新互联行业资讯频道,感谢各位的阅读!