本文引自:http://www.javashuo.com/article/p-uduvhkdb-cd.htmlhtml
这篇是对angularJS的一些疑点回顾,是对目前angularJS开发的各类常见问题的整理汇总。若是对文中的题目所有了然于胸,以为对整个angular框架应该掌握的七七八八了。但愿志同道合的通知补充内容前端
脏检查机制。阐释脏检查机制,必须先了解以下问题。git
ng-bind 单向数据绑定($scope -> view),用于数据显示,简写形式是 {{}}。angularjs
二者的区别在于页面没有加载完毕 {{val}} 会直接显示到页面,直到 Angular 渲染该绑定数据(这种行为有可能将 {{val}} 让用户看到);而 ng-bind 则是在 Angular 渲染完毕后将数据显示。github
ng-model 是双向数据绑定(scope−>viewandview−>scope−>viewandview−>scope),用于绑定值会变化的表单元素等。web
双向数据绑定是 AngularJS 的核心机制之一。当 view 中有任何数据变化时,会更新到 model ,当 model 中数据有变化时,view 也会同步更新,显然,这须要一个监控。bootstrap
Angular 在 scope 模型上设置了一个 监听队列,用来监听数据变化并更新 view 。segmentfault
每次绑定一个东西到 view 上时 AngularJS 就会往 watch队列里插入一条watch队列里插入一条watch,用来检测它监视的 model 里是否有变化的东西。 数组
当你写下表达式如{{ val }}时,AngularJS在幕后会为你在scope模型上设置一个watcher(表达式将被 Angular 编译成一个监视函数),它用来在数据发生变化的时候更新view。这里的watcher和你会在AngularJS中设置的watcher是同样的:浏览器
1
2
3
|
$scope.$watch(
'val'
,
function
(newValue, oldValue) {
//update the DOM with newValue
});
|
将数据附加到 Scope 上,数据自身不会对性能产生影响,若是没有监视器来监视这个属性,那个这个属性在不在 Scope 上是无关重要的;Angular 并不会遍历 Scope 上的属性,它将遍历全部的观察器。
每一个监视函数是在每次 $digest 过程当中被调用的。所以,咱们要注意观察器的数量以及每一个监视函数或者监视表达式的性能。
当浏览器接收到能够被 angular context 处理的事件时,digest循环就会触发,遍历所有的digest循环就会触发,遍历全部的watch,最后更新 dom。
举个栗子
1
|
<
button
ng-click="val=val+1">increase 1</
button
>
|
click 时会产生一次更新的操做(至少触发两次 $digest 循环)
按下按钮
浏览器接收到一个事件,进入到 angular context
digest循环开始执行,查询每个digest循环开始执行,查询每一个watch 是否变化
因为监视 scope.val的scope.val的watch 报告了变化,所以强制再执行一次 $digest 循环
新的 $digest 循环未检测到变化
浏览器拿回控制器,更新 $scope. val.新值对应的 dom
在调用了scope.scope.digest()后,digest循环就开始了。假设你在一个ng−click指令对应的handler函数中更改了scope中的一条数据,此时AngularJS会自动地通过调用digest循环就开始了。假设你在一个ng−click指令对应的handler函数中更改了scope中的一条数据,此时AngularJS会自动地经过调用digest()来触发一轮digest循环。当digest循环。当digest循环开始后,它会触发每一个watcher。这些watchers会检查scope中的当前model值是否和上一次计算获得的model值不一样。若是不一样,那么对应的回调函数会被执行。调用该函数的结果,就是view中的表达式内容(译注:诸如{{ val }})会被更新。除了ng-click指令,还有一些其它的built-in指令以及服务来让你更改models(好比ng-model,timeout等)和自动触发一次timeout等)和自动触发一次digest循环。
目前为止还不错!可是,有一个小问题。在上面的例子中,AngularJS并不直接调用digest(),而是调用digest(),而是调用scope.apply(),后者会调用apply(),后者会调用rootScope.$digest()。所以,一轮digest循环在digest循环在rootScope开始,随后会访问到全部的children scope中的watchers。
一般写代码时咱们无需主动调用 apply或apply或digest 是由于 angular 在外部对咱们的回调函数作了包装。例如经常使用的 ng-click,这是一个指令(Directive),内部实现则 相似 于
1
2
3
|
DOM.addEventListener(
'click'
,
function
($scope) {
$scope.$apply(() => userCode());
});
|
能够看到:ng-click 帮咱们作了 apply这个操做。类似的不只是这些事件回调函数,还有apply这个操做。相似的不仅是这些事件回调函数,还有http、timeout等。我听很多人抱怨说angular这个库太大了什么都管,其实你可以不用它自带的这些服务(Service),只要你记得手工调用timeout等。我听不少人抱怨说angular这个库太大了什么都管,其实你能够不用它自带的这些服务(Service),只要你记得手工调用scope.$apply。
如今,假设你将ng-click指令关联到了一个button上,并传入了一个function名到ng-click上。当该button被点击时,AngularJS会将此function包装到一个wrapping function中,而后传入到scope.scope.apply()。所以,你的function会正常被执行,修改models(若是须要的话),此时一轮$digest循环也会被触发,用来确保view也会被更新。
Note: scope.scope.apply()会自动地调用rootScope.rootScope.digest()。apply()方法有两种形式。第一种会接受一个function做为参数,执行该function并且触发一轮apply()方法有两种形式。第一种会接受一个function做为参数,执行该function而且触发一轮digest循环。第二种会不接受任何参数,只是触发一轮$digest循环。咱们立刻会看到为何第一种形式更好。
$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),内部实现则 相似于
1
2
3
|
DOM.addEventListener(
'click'
,
function
($scope) {
$scope.$apply(() => userCode());
});
|
angular对经常使用的dom事件,xhq事件做了封装,若是调用这些封装,就会在里面触发进入angular的digest流程,主要有如下状况:
DOM事件,如用户输入文本,点击按钮等,(ng-click)
XHQ响应事件($http)
浏览器Location变动事件,即Url中hash部分变动($location)
Timer事件(Timeout,Timeout,interval)
手动调用apply或apply或digest
apply是apply是scope(或者是 direcvie 里的 link 函数中的 scope)的一个函数,调用它会强制一次 digest循环(除非当前正在执行循环,这种情况下会抛出一个异常,这是我们不需要在那里执行digest循环(除非当前正在执行循环,这种状况下会抛出一个异常,这是咱们不须要在那里执行apply 的标志)。
apply()和apply()和digest() 有两个区别。
1) 最直接的差别是, $apply 能够带参数,它能够接受一个函数,而后在应用数据以后,调用这个函数。因此,通常在集成非 Angular 框架(好比jQuery)的代码时,能够把代码写在这个里面调用。
2) 当调用 digest的时候,只触发当前做用域和它的子做用域上的监控,但是当调用digest的时候,只触发当前做用域和它的子做用域上的监控,可是当调用apply 的时候,会触发做用域树上的全部监控。
取决因而否在 Angular 上下文环境(angular context)。
AngularJS对此有着很是明确的要求,就是它只负责对发生于AngularJS上下文环境中的变动会作出自动地响应(即,在apply()方法中发生的对于models的更改)。AngularJS的built−in指令就是这样作的,所以任何的model变更都会被反映到view中。但是,如果你在AngularJS上下文之外的任何地方修改了model,那么你就需要