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

IT技术 javascript angularjs
2021-02-06 01:02:16

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

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

4个回答

$observe()Attributes对象上的一个方法,因此它只能用于观察/观察 DOM 属性的值变化。它仅在指令内部使用/调用。当您需要观察/观察包含插值(即 {{}} 的)的 DOM 属性时,请使用 $observe。
例如attr1="Name: {{name}}",然后在指令:attrs.$observe('attr1', ...)
(如果你尝试scope.$watch(attrs.attr1, ...)它不会工作,因为 {{}} s - 你会得到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')你会得到 string myModel.some_prop,这可能不是你想要的。)

正如对@PrimosK 的回答的评论中所讨论的那样,每个摘要周期都会检查所有 $observes 和 $watches

具有隔离作用域的指令更复杂。如果使用“@”语法,您可以 $observe或 $watch包含插值(即 {{}} 的)的 DOM 属性。(它与 $watch 一起工作的原因是因为 '@' 语法为我们做了插值,因此 $watch 看到一个没有 {{}} 的字符串。)为了更容易记住什么时候使用哪个,我建议使用$observe 也适用于这种情况。

为了帮助测试所有这些,我编写了一个定义两个指令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关于 $watch 表达式的 google group 帖子

你能比较 scope.$eval 和 scope.$evalAsync,当我想将对象表达式作为指令参数传递时,我有时会使用它们。像<div my-directive="{prop: model.prop}"></div>。它允许我将所有配置参数放入一个属性中。
2021-03-15 01:02:16
@tamakisquare,它们在使用@语法时可以互换我相信没有性能差异(但我没有看过实际的源代码)。
2021-03-28 01:02:16
谢谢你的好帖子。scope.$eval(item) 真的很有帮助。如果 item 是 json 字符串,则转换为 json 对象。
2021-03-30 01:02:16
+1 对于 AngularJS 教皇!每次我在 Stack 中搜索有关我最新的 Angular 问题的一些信息时,我都不可避免地最终会阅读@MarkRajcok 接受的答案。
2021-04-03 01:02:16
很好的答案!你知道为什么要ng-src/ng-href使用attr.$observe而不是scope.$watchthen 吗?
2021-04-11 01:02:16

如果我理解您的问题是正确的,您会问$watch如果您使用$observe.

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

$observe当包含插值(例如attr="{{notJetInterpolated}}"的属性的值发生变化时将调用注册的回调


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

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

或者

    scope.$watch(attrs['attrYouWatch'], function() {
         // body
    });
@fastreload...感谢您的精彩解释。如果我理解正确的话,观察者是用于角度表达式的。我对吗?
2021-03-21 01:02:16
@fastreload,我完全同意你的评论.. 写得很好!
2021-03-29 01:02:16
@PrimosK:为我之前的评论添加你。
2021-04-03 01:02:16
@Abilash 观察者用于观察 dom 属性,而不仅仅是表达式。所以如果你自己改变了属性值,它会在下一个摘要周期中反映出来。
2021-04-03 01:02:16
实际上,由于每个更改都反映在$digest阶段,因此可以安全地假设$observe回调将在$digest. 并且$watch回调也将被调用,$digest但每当值更改时。我认为他们做了完全相同的工作:“观察表达式,调用回调值更改”。关键字差异可能只是为了不混淆开发人员的语法糖。
2021-04-06 01:02:16

我认为这很明显:

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

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

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

我制作了一个plunker,因此您实际上可以掌握它们的使用情况。我使用了变色龙的比喻,以便更容易地描绘出来。

我认为参数可能会被切换 - 它似乎将 newValue,然后 oldValue 传递给 attrs.$observe() 。. .
2021-03-29 01:02:16
它的用法很明显。但问题是为什么。马克总结得很好。
2021-04-06 01:02:16

为什么 $observe 与 $watch 不同?

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

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

compile.js 中的$observe & $set