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

Abi*_*ash 375 javascript angularjs

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

我最初的理解是Observers为角度表达式计算,这是HTML一侧的条件,Watchers执行$scope.$watch()函数时执行.我在想什么?

Mar*_*cok 606

$ observe() Attributes对象上的一个方法,因此,它只能用于观察/观察DOM属性的值更改.它仅在指令内使用/调用.需要观察/观察包含插值的DOM属性时使用$ observe(即{{}}).
例如,attr1="Name: {{name}}"然后在指令中:attrs.$observe('attr1', ...).
(如果你尝试scope.$watch(attrs.attr1, ...)它会因为{{}}而无法工作 - 你会得到的undefined.)使用$ watch来做其他事情.

$ watch()更复杂.它可以观察/观察"表达式",其中表达式可以是函数或字符串.如果表达式是一个字符串,则它是 $ parse'd(即,作为 Angular表达式计算)到函数中.(每个摘要周期都会调用此函数.)字符串表达式不能包含{{}}.$ watch是 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和$ watch .

具有隔离范围的指令更复杂.如果使用'@'语法,你可以$ observe 或$ watch一个包含插值的DOM属性(即{{}}').(它与$ watch一起使用的原因是因为'@'语法为我们进行插值,因此$ watch会看到一个没有{{}}的字符串.)为了让它更容易记住何时使用,我建议使用$观察这个案子也.

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

<div d1 attr1="{{prop1}}-test" attr2="prop2" attr3="33" attr4="'a_string'"
        attr5="a_string" attr6="{{1+aNumber}}"></div>
Run Code Online (Sandbox Code Playgroud)

查看控制台日志以查看链接函数中$ observe和$ watch之间的差异.然后单击该链接,查看由Click处理程序进行的属性更改触发的$ observes和$ watches.

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

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

另请参阅Vojta关于$ watch表达式的谷歌小组帖子.

  • 这比官方的Angular文档更有帮助!谢谢! (53认同)
  • 很棒的解释!+1 (13认同)
  • @tamakisquare,使用`@`语法时它们是可互换的.我相信没有性能差异(但我没有看过实际的源代码). (5认同)
  • 很棒的答案!你知道为什么`ng-src/ng-href`使用`attr.$ observe`而不是`scope.$ watch`然后呢? (4认同)
  • +1为AngularJS教皇!每当我搜索Stack有关我最新Angular问题的一些信息时,我不可避免地最终会阅读@MarkRajcok接受的答案. (4认同)

Pri*_*osK 23

如果我理解你的问题,你就会问,如果你注册听众回调,$watch或者如果你这样做,你会有什么不同$observe.

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

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


在内部指令中,您可以以非常类似的方式使用它们:

    attrs.$observe('attrYouWatch', function() {
         // body
    });
Run Code Online (Sandbox Code Playgroud)

要么

    scope.$watch(attrs['attrYouWatch'], function() {
         // body
    });
Run Code Online (Sandbox Code Playgroud)

  • 实际上,由于每个更改都反映在`$ digest`阶段,因此可以安全地假设`$ observe`回调将在`$ digest`中调用.并且`$ watch`回调也将在`$ digest`中调用,但每当值改变时.我认为他们完成同样的工作:"观察表达式,调用值回调的回调".关键字差异可能只是语法糖,不会让开发人员感到困惑. (3认同)
  • @Abilash观察员用于观察dom属性,而不仅仅是表达式.因此,如果您自己更改属性值,它将反映在下一个摘要周期中. (2认同)