AngularJS: $observe和$watch方法的区别

我知道一旦AngularJS中$scope中的某些东西发生变化,WatchersObservers都会被计算出来。但不明白两者到底有什么区别。

我最初的理解是,Observers是为角表达式计算的,角表达式是HTML端上的条件,当$scope.$watch()函数执行时执行Watchers。我想得对吗?

126965 次浏览

如果我理解你的问题正确,你问的是,如果你用$watch注册监听器回调,或者用$observe注册,有什么不同。

在执行$digest时触发与$watch注册的回调。

$observe注册的回调函数在包含插值的属性值更改时被调用(例如attr="\{\{notJetInterpolated}}")。


内部指令你可以用非常相似的方式使用它们:

    attrs.$observe('attrYouWatch', function() {
// body
});

    scope.$watch(attrs['attrYouWatch'], function() {
// body
});
< a href = " https://docs.angularjs.org/api/ng。compile.directive.Attributes #观察美元”>观察美元()< / >属性对象上的一个方法,因此,它只能用于观察/观察DOM属性的值变化。它只在指令内部使用/调用。当你需要观察/观察一个包含插值(即\{\{}})的DOM属性时,使用$observe。 < br >。, attr1="Name: \{\{name}}",然后在一个指令中:attrs.$observe('attr1', ...)
(如果你尝试scope.$watch(attrs.attr1, ...),它不会工作,因为\{\{}}-你会得到undefined。)使用$watch进行其他操作。

< a href = " http://docs.angularjs.org/api/ng。rootScope.Scope #看美元”>看美元()< / >更复杂。它可以观察/观察一个“表达式”,其中表达式可以是一个函数或字符串。如果表达式是字符串,则将其__abc1d(即计算为角表达式)转换为函数。(每个文摘周期都要调用这个函数。)字符串表达式不能包含\{\{}}。$watch是范围对象上的一个方法,因此可以在任何可以访问范围对象的地方使用/调用它,因此在

  • 一个控制器——任何控制器——通过ng-view、ng-controller或指令控制器创建的控制器
  • 指令中的链接函数,因为它也可以访问作用域
因为字符串是按Angular表达式求值的,所以当你想观察/观察一个模型/作用域属性时,通常会使用$watch。例如,attr1="myModel.some_prop",然后在控制器或链接函数中:scope.$watch('myModel.some_prop', ...)scope.$watch(attrs.attr1, ...)(或scope.$watch(attrs['attr1'], ...))。
(如果你尝试attrs.$observe('attr1'),你会得到字符串myModel.some_prop,这可能不是你想要的。)

正如在@PrimosK的回答的评论中所讨论的,所有的$ observed和$watches每消化周期都会检查一次。

具有独立作用域的指令更加复杂。如果使用'@'语法,你可以$observe 或看美元一个包含插值(即\{\{}})的DOM属性。(它与$watch一起工作的原因是因为'@'语法为我们做了插值,因此$watch看到一个没有\{\{}}的字符串。)为了更容易记住何时使用哪个,我建议在这种情况下也使用$observe。

为了帮助测试所有这些,我写了一个砰砰作响,它定义了两个指令。一个(d1)不创建新的作用域,另一个(d2)创建一个隔离作用域。每个指令都有六个相同的属性。每个属性都是$ observed和$watch'ed。

<div d1 attr1="\{\{prop1}}-test" attr2="prop2" attr3="33" attr4="'a_string'"
attr5="a_string" attr6="\{\{1+aNumber}}"></div>

查看控制台日志,查看链接函数中$observe和$watch之间的差异。然后单击链接,查看哪些$ observed和$watches是由单击处理程序所做的属性更改触发的。

请注意,当link函数运行时,任何包含\{\{}}的属性都不会计算(因此如果尝试检查属性,将得到undefined)。查看插值值的唯一方法是使用$observe(如果使用带有'@'的隔离作用域,则使用$watch)。因此,获取这些属性的值是一个异步操作。(这就是为什么我们需要$observe和$watch函数。)

有时你不需要$observe或$watch。例如,如果你的属性包含一个数字或布尔值(不是字符串),只计算它一次:attr1="22",然后在,比如说,你的链接函数:var count = scope.$eval(attrs.attr1)。如果它只是一个常量字符串–attr1="my string" mdash;然后在你的指令中使用attrs.attr1(不需要$eval())。

参见Vojta的谷歌群帖子中的$watch表达式。

我认为这很明显:

  • $observe用于指令的链接函数。
  • $watch在scope上用于监视其值的任何变化。

记住:两个函数都有两个参数,

$observe/$watch(value : string, callback : function);
  • 价值:始终是被监视元素的字符串引用(作用域变量的名称或要监视的指令属性的名称)
  • 回调:要以function (oldValue, newValue)形式执行的函数

我已经做了一个< kbd >恰好< / kbd >,所以你实际上可以掌握它们的使用情况。我用变色龙的比喻是为了更容易描绘。

为什么$observe和$watch不同?

在每个digest()循环中,对watchExpression进行计算并与前一个值进行比较,如果watchExpression值发生变化,则调用watch函数。

$observe专门用于观察插入的值。如果一个指令的属性值被插入,例如dir-attr="\{\{ scopeVar }}",则只有在插入的值被设置时才会调用observe函数(因此当$digest已经确定需要进行更新时)。基本上已经有了一个用于插值的观察者,而$observe函数与之相连。

参见$observe &在compile.js中设置$