上海分院:顾配如
1.背景介绍
2.知识剖析
3.常见问题
4.解决方案
5.编码实战
6.扩展思考
7.参考文献
8.更多讨论
双向数据绑定是 AngularJS 的核心机制之一。当 view 中有任何数据变化时,会更新到 model ,当 model 中数据有变化时,view 也会同步更新
Angular 在 scope 模型上设置了一个 监听队列,用来监听数据变化并更新 view 。 每次绑定一个东西到 view 上时 AngularJS 就会往 watch队列里插入一条 watch队列里插入一条watch,用来检测它监视的
model 里是否有变化的东西。
当你写下表达式如{{ val }}时,AngularJS在幕后会为你在scope模型上设置一个watcher(表达式将被 Angular 编译成一个监视函数),它用来在数据发生变化的时候更新view,
这里的watcher和你会在AngularJS中设置的watcher是一样的:
•将数据附加到 Scope 上,数据自身不会对性能产生影响,如果没有监视器来监视这个属性,那个这个属性在不在 Scope 上是无关重要的;Angular 并不会遍历 Scope 上的属性,它将遍历所有的观察器。
•每个监视函数是在每次 $digest 过程中被调用的。因此,我们要注意观察器的数量以及每个监视函数或者监视表达式的性能。
Angular双向绑定通过watch,digest和apply实现的。
watch监控model中是否有变化,会记录last值,也就是改变后的值,每一个model都会增加一个watch到watch队列中。
传递给$watch()的第二个参数称为监听器函数,当aModel的值发生变化时,它就被调用。我们很容易理解,当aModel的值发生改变,这个监听器就会被调用来更新HTML中的表达式。但是,还有一个很重要的问题!Angular是怎么判断什么时候调用这个监听器函数的呢?换句话说,AngularJS是如何知道aModel值是何时发生改变的,从而它可以调用相应的监听器函数呢?它是否定期运行一个函数来检查 scope 模型的值是否已经改变了?好,这就是 $digest 循环的步骤。
在 $digest 周期中,watcher 会被触发。当一个 watcher 被触发时,AngularJS将评估 scope 模型,如果它发生了变化,则调用相应的监听器函数。那么,我们的下一个问题是,这个 $digest 循环是何时开始的。
当浏览器接收到可以被 angular context 处理的事件时,digest循环就会触发,,遍历所有的watch,最后更新 dom。假设你通过ng-click指令在处理程序函数中更改了一个scope模型。在这种情况下,AngularJS会通过调用 $digest() 自动触发一个 $digest 循环。当 $digest 循环开始的时候,它就会触发每一个 watcher。这些 watcher 会检查scope模型的当前值是否与上次计算得到的值不同。如果不同,则执行相应的监听器函数。因此,如果在视图中有任何表达式,它们将被更新。除了ng-click之外,还有其他一些内置的指令/服务可以让你更改模型(例如ng-model、$timeout 等),并自动触发一个 $digest 循环。
click 时会产生一次更新的操作(至少触发两次 $digest 循环)
•按下按钮 •浏览器接收到一个事件,进入到 angular context
•digest循环开始执行,查询每个watch 是否变化
•由于监视 scope.val的 scope.val的watch 报告了变化,因此强制再执行一次 $digest 循环
•新的 $digest 循环未检测到变化 •浏览器拿回控制器,更新 $scope. val.新值对应的 dom
那我们到底什么时候需要去调用apply()方法呢?情况非常少,实际上几乎我们所有的代码都包在scope.apply()里面,像ng−click,controller的初始化,http的回调函数等。在这些情况下,我们不需要自己调用,实际上我们也不能自己调用,否则在apply()方法里面再调用 apply()方法会抛出错误。如果我们需要在一个新的执行序列中运行代码时才真正需要用到它,而且当且仅当这个新的执行序列不是被angular JS的库的方法创建的,这个时候我们需要将代码用 scope.apply()包起来。
$digest 循环的上限是 10 次(超过 10次后抛出一个异常,防止无限循环)。 $digest 循环不会只运行一次。在当前的一次循环结束后,它会再执行一次循环用来检查是否有 models 发生了变化。 这就是脏检查(Dirty Checking),它用来处理在 listener 函数被执行时可能引起的 model 变化。因此 digest循环会持续运行直到model不再发生变化,或者 digest循环会持续运行直到model不再发生变化,或者digest 循环的次数达到了 10 次(超过 10 次后抛出一个异常,防止无限循环)。 当 $digest 循环结束时,DOM 相应地变化。
angular 会在可能触发 UI 变更的时候进行脏检查:这句话并不准确。实际上脏检查是digest执行的,另一个更常用的用于触发脏检查的函数apply——其实就是 $digest 的一个简单封装(还做了一些抓异常的工作)。 通常写代码时我们无需主动调用 apply或 apply或digest 是因为 angular 在外部对我们的回调函数做了包装。例如常用的 ng-click,这是一个指令(Directive),$digest过程的逻辑就是检查watcher列表中的每一项,看当前值与上次的值是否相同,如果不同则调用listener回调函数。这就是dirty-checking的核心逻辑,
apply是 apply是scope(或者是 direcvie 里的 link 函数中的 scope)的一个函数,调用它会强制一次 digest循环(除非当前正在执行循环,这种情况下会抛出一个异常,这是我们不需要在那里执行 digest循环(除非当前正在执行循环,这种情况下会抛出一个异常,这是我们不需要在那里执行apply 的标志)。
https://www.cnblogs.com/zhoulujun/p/8881414.html
BY——顾配如