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


378

我知道,无论WatchersObservers就将计算在东西$scope在AngularJS变化。但是无法理解两者之间到底有什么区别。

我最初的理解是Observers为角表达式计算的,这些角表达式是HTML端在Watchers执行$scope.$watch()函数时所执行的条件。我的想法是否正确?


1
您的编辑没有帮助,而且有点令人讨厌。请体谅其他人来这里寻求实际帮助。
smalone

@smalone已更改。谢谢,抱歉!
阿比拉什

👍不用担心。感谢您修复。
smalone

Answers:


608

$ observe() Attributes对象上的一种方法,因此,它只能用于观察/监视DOM属性的值更改。仅用于/调用内部指令。需要观察/观察包含插值的DOM属性(即{{}})时,请使用$ observe。
例如,attr1="Name: {{name}}"然后在指令中:attrs.$observe('attr1', ...)
(如果您尝试scope.$watch(attrs.attr1, ...)使用{{}},它将无法工作-您将得到undefined。)将$ watch用于其他所有内容。

$ watch()更复杂。它可以观察/观察“表达式”,其中表达式可以是函数或字符串。如果表达式是字符串,则将 $ parse'd(即,作为 Angular表达式求值)放入函数中。(每个摘要周期都会调用此函数。)字符串表达式不能包含{{}}。$ watch是 Scope对象上的一种方法,因此可以在任何有权访问scope对象的地方使用/调用它,因此在

  • 一个控制器-任何控制器-一个通过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答案的评论所讨论的,每个摘要周期都会检查所有$ observes和$ watches 。

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

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

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

查看控制台日志,以查看链接功能中$ observe和$ watch之间的区别。然后单击链接,查看单击处理程序进行的属性更改触发了哪些$ observes和$ watches。

请注意,当链接功能运行时,尚未评估任何包含{{}}的属性(因此,如果您尝试检查这些属性,则会得到undefined)。查看插值的唯一方法是使用$ observe(如果使用带'@'的隔离范围,则使用$ watch)。因此,获取这些属性的值是异步操作。(这就是为什么我们需要$ observe和$ watch函数。)

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

另请参见Vojta在Google网上论坛上发布的有关$ watch表达式的信息。


13
很好的解释!+1
PrimosK

4
好答案!您知道为什么要ng-src/ng-href使用attr.$observe而不是scope.$watch
okm

4
+1为AngularJS教皇!每次我在Stack上搜索有关最新Angular问题的信息时,都会不可避免地阅读@MarkRajcok接受的答案。
GFoley83

1
感谢您的精彩文章。scope。$ eval(item)真的很有帮助。如果item是json字符串,则将其转换为json对象。
bnguyen82

5
@tamakisquare,使用@语法时它们可以互换。我相信没有性能差异(但是我没有查看实际的源代码)。
Mark Rajcok 2013年

25

如果我正确理解了您的问题,那么您在问,如果使用注册注册侦听器回调$watch或使用进行注册,有什么区别$observe

执行$watch时触发向注册的回调$digest

$observe当包含插值的属性的值更改时(例如attr="{{notJetInterpolated}}"),将调用注册到的回调。


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

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

要么

    scope.$watch(attrs['attrYouWatch'], function() {
         // body
    });

3
实际上,由于每个更改都可以分阶段反映出来$digest,因此可以安全地假设将在中$observe调用回调$digest。并且$watch回调也会被调用,$digest但是每当值改变时。我认为他们做的完全一样:“观察表达式,调用回调值的更改”。关键字差异可能只是语法糖,不会使开发人员感到困惑。
UmurKontacı13年

1
@fastreload,我完全同意您的评论。
PrimosK

@fastreload ...感谢您的精彩解释。如果我理解正确的话,观察者就是角度表达式。我对吗?
2013年

@PrimosK:添加您作为我之前的评论。
Abilash

2
@Abilash观察者用于观察dom属性,而不仅仅是表达式。因此,如果您自己更改属性值,它将反映在下一个摘要周期中。
UmurKontacı13年

1

我认为这很明显:

  • $ observe用于指令的链接功能。
  • $ watch用于观察值的任何变化。

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

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

我已经做了一个plunker,所以您实际上可以了解它们的利用率。我使用了变色龙类比,以使其更容易描绘。


2
它的用法非常明显。但是,为什么呢?马克总结得很漂亮。
Abilash

3
我认为参数可能会切换-它似乎传递newValue,然后将oldValue传递给attrs。$ observe()。。。
blaster

0

为什么$ observe与$ watch不同?

评估watchExpression并将其与每个digest()周期的前一个值进行比较,如果watchExpression值发生变化,则会调用watch函数。

$ observe专门用于监视插值。如果插入指令的属性值,例如dir-attr="{{ scopeVar }}",则仅在设置了插入值时(因此,在$ digest已确定需要进行更新时),将调用watch函数。基本上,已经有一个插值的监视程序,并且$ observe函数支持此操作。

请参见compile.js中的 $ observe和$ set

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.