通过脏检查$scope
对象
Angular 在对象中维护了一个简单array
的观察者。$scope
如果您检查任何$scope
内容,您会发现它包含一个array
被调用的$$watchers
.
每个观察者都是一个object
包含其他内容的
- 观察者正在监视的表达式。这可能只是一个
attribute
名称,或者更复杂的东西。
- 表达式的最后一个已知值。这可以根据表达式的当前计算值进行检查。如果值不同,观察者将触发该函数并将其标记
$scope
为脏。
- 如果观察者脏了将执行的函数。
如何定义观察者
在 AngularJS 中有许多不同的方法来定义观察者。
您可以明确$watch
地attribute
on $scope
。
$scope.$watch('person.username', validateUnique);
您可以{{}}
在模板中放置一个插值(将在 current 上为您创建一个观察者$scope
)。
<p>username: {{person.username}}</p>
您可以要求一个指令,例如为ng-model
您定义观察者。
<input ng-model="person.username" />
$digest
循环检查所有观察者的最后一个值
当我们通过正常渠道(ng-model、ng-repeat 等)与 AngularJS 交互时,指令将触发一个摘要循环。
摘要循环是深度优先遍历$scope
及其所有子项。对于每个$scope
object
,我们对其进行迭代$$watchers
array
并评估所有表达式。如果新的表达式值与最后一个已知值不同,则调用观察者的函数。这个函数可能会重新编译 DOM 的一部分,重新计算 on 的值$scope
,触发 an AJAX
request
,以及任何你需要它做的事情。
遍历每个作用域,并根据最后一个值评估和检查每个监视表达式。
如果触发了观察者,$scope
则为脏
如果触发了观察者,则应用程序知道某些内容发生了变化,并将其$scope
标记为脏。
Watcher 函数可以更改父级$scope
或父级上的其他属性$scope
。如果一个$watcher
函数被触发了,我们不能保证我们的其他$scope
s仍然是干净的,所以我们再次执行整个摘要循环。
这是因为 AngularJS 有双向绑定,所以数据可以传回$scope
树。$scope
我们可能会更改已经消化的更高值。也许我们改变了$rootScope
.
如果$digest
是脏的,我们再次执行整个$digest
循环
我们不断地循环$digest
循环,直到摘要循环干净(所有$watch
表达式的值与前一个循环中的值相同),或者我们达到摘要限制。默认情况下,此限制设置为 10。
如果我们达到摘要限制,AngularJS 将在控制台中引发错误:
10 $digest() iterations reached. Aborting!
摘要在机器上很难,但对开发人员来说很容易
如您所见,每当 AngularJS 应用程序发生变化时,AngularJS 都会检查$scope
层次结构中的每个观察者以查看如何响应。对于开发人员来说,这是一个巨大的生产力提升,因为您现在几乎不需要编写任何接线代码,AngularJS 只会注意到值是否已更改,并使应用程序的其余部分与更改保持一致。
从机器的角度来看,虽然这非常低效,并且如果我们创建太多的观察者会减慢我们的应用程序。Misko 引用了大约 4000 名观察者的数据,您的应用程序在旧浏览器上会感觉很慢。
ng-repeat
例如,如果您超过一个大的,这个限制很容易达到JSON
array
。您可以使用一次性绑定等功能来缓解这种情况,以在不创建观察者的情况下编译模板。
如何避免创建过多的观察者
每次您的用户与您的应用交互时,您应用中的每个观察者都将至少被评估一次。优化 AngularJS 应用程序的一个重要部分是减少$scope
树中观察者的数量。一种简单的方法是一次性绑定。
如果您有很少更改的数据,则可以使用 :: 语法将其绑定一次,如下所示:
<p>{{::person.username}}</p>
或者
<p ng-bind="::person.username"></p>
只有在渲染包含模板并将数据加载到$scope
.
ng-repeat
当您拥有许多物品时,这一点尤其重要。
<div ng-repeat="person in people track by username">
{{::person.username}}
</div>