我知道Watchers和Observers都是在$scope的角度发生变化时计算出来的。但不明白这两者到底有什么区别。
我最初的理解是,Observers是为角表达式计算的,这些角表达式是HTML端的条件,当执行$scope.$watch()函数时,执行Watchers。我想得对吗?
- 你的编辑没有帮助,有点让人反感。请体谅前来寻求实际帮助的其他人。
- @斯马龙变了。谢谢,对不起!
- ?不用担心。谢谢你的修理。
$observer()是attributes对象上的一个方法,因此,它只能用于观察/监视dom属性的值更改。它只在指令内部使用/调用。当需要观察/观察包含插值(即123; s)的DOM属性时,请使用$observer。例如,attr1="Name: {{name}}",然后在一个指令中:attrs.$observe('attr1', ...)。(如果你尝试使用scope.$watch(attrs.attr1, ...),它将不起作用,因为s——你将得到undefined)。使用$watch来处理其他所有事情。
$watch()更复杂。它可以观察/观察"表达式",其中表达式可以是函数或字符串。如果表达式是一个字符串,它是$parse'd(即,作为一个角表达式计算)到一个函数中。(每个摘要循环都调用这个函数。)字符串表达式不能包含s。$watch是作用域对象上的一个方法,因此可以在您有权访问作用域对象的任何位置使用/调用它,因此在
- 一个控制器——任何控制器——通过ng视图、ng控制器或指令控制器创建的控制器。
- 指令中的链接函数,因为它也可以访问范围
因为字符串的计算是角度表达式,所以当您希望观察/观察模型/范围属性时,通常使用$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答案的评论中所讨论的,所有$observers和$watches在每个摘要循环中都会被检查。
带有隔离作用域的指令更加复杂。如果使用了"@"语法,则可以$observer或$watch包含插值的dom属性(即,s)。(它与$watch一起使用的原因是,@'语法为我们做了插值,因此$watch看到一个没有s的字符串)为了更容易记住在什么时候使用,我建议在这种情况下也使用$watch。
为了帮助测试所有这些,我编写了一个punker,它定义了两个指令。一个(d1不创建新作用域,另一个(d2创建独立作用域。每个指令具有相同的六个属性。每个属性都是$observer'd和$watch'ed。
1 2
| <div d1 attr1="{{prop1}}-test" attr2="prop2" attr3="33" attr4="'a_string'"
attr5="a_string" attr6="{{1+aNumber}}"> |
查看控制台日志,查看链接函数中$observe和$watch之间的差异。然后单击链接,查看哪些$observers和$watches是由单击处理程序所做的属性更改触发的。
注意,当link函数运行时,包含的任何属性都不会被评估(因此,如果尝试检查这些属性,您将得到undefined)。查看插值值的唯一方法是使用$observer(如果使用带"@"的隔离范围,则使用$watch)。因此,获取这些属性的值是一个异步操作。(这就是为什么我们需要$observer和$watch功能。)
有时你不需要$observe或$watch。例如,如果属性包含数字或布尔值(不是字符串),只需计算一次:attr1="22",然后在(例如)链接函数中:var count = scope.$eval(attrs.attr1)。如果它只是一个常量字符串&ndash;attr1="my string"&ndash;,那么只需在指令中使用attrs.attr1(不需要$eval())。
另请参见Vojta的Google Group帖子关于$watch表达式。
- 很好的解释!+ 1
- 这比官方的角度文件更有用!谢谢!
- 伟大的回答!你知道为什么ng-src/ng-href使用attr.$observe而不是scope.$watch吗?
- @OKM,因为它们不创建隔离作用域(源代码),所以它们必须使用$observe,因为属性值包含{{}}。
- +一个给安古拉基斯教皇!每次我在堆栈中搜索关于我最新角度问题的信息时,我都不可避免地会看到@markrajcok接受的答案。
- 谢谢你的精彩帖子。范围。$eval(item)非常有用。如果项是JSON字符串,它将转换为JSON对象。
- 你能比较scope.$eval和scope.$evalasync吗,当我想把对象表达式作为指令参数传入时,我有时会使用它们。喜欢。它允许我将所有配置参数放到一个属性中。
- @cmcdragonkai,$eval立即计算表达式(在当前作用域上),而$evalasync将计算表达式"稍后",但在浏览器呈现之前,如果需要等待angular在计算表达式之前进行一些DOM更新,这有时很有用。另请参见stackoverflow.com/a/17239084/215945和stackoverflow.com/a/17303759/215945。
- @Markrajcok-谢谢你的回答。如果我理解正确,对于具有独立作用域的指令,$observe和$watch可以互换使用。对吗?由于观察者和观察者在每个消化周期都会被检查,我可以安全地说,$observe和$watch在性能上没有区别吗?
- @Tamakisquare,它们在使用@语法时可以互换。我相信没有性能差异(但我没有查看实际的源代码)。
- @马克拉乔克-非常感谢。这比我读过的任何其他信息来源都清楚得多。再次感谢。
- @马克·拉吉科克,这真是太神奇了,不过你得花点时间才能清醒过来。
- 我在下面的要点中发现了一些有趣的案例,其中需要监视属性。gist.github.com/cmcdragonkai/6282750
- @markrajcok和@tamakisquare,从文档中,它可能会建议$observer更具性能,也更准确,至少在与$compile一起使用时:"使用$observe观察包含插值的属性的值变化(例如src="{{bar}}")。这不仅是非常有效的,而且也是获得实际值的唯一方法,因为在连接阶段,尚未对插值进行评估,因此此时该值设置为undefined。
- @这真是太神奇了,但看起来安古拉吉斯自2013年以来有了一些变化。我更新了你的PLNKR拉入角度1.4.5(没有对你的代码做任何其他更改)……plnkr.co/edit/ix4gurakellkasbtt?P =预览…控制台的输出不同…你能谈谈发生了什么变化吗?
如果我正确理解你的问题,你会问如果你用$watch注册监听回调,或者用$observe注册监听回调有什么不同。
当执行$digest时,将激发用$watch注册的回调。
当包含插值的属性(如attr="{{notJetInterpolated}}"的值发生变化时,调用注册到$observe的回调。
在指令中,您可以以非常相似的方式使用这两种方法:
1 2 3
| attrs.$observe('attrYouWatch', function() {
// body
}); |
或
1 2 3
| scope.$watch(attrs['attrYouWatch'], function() {
// body
}); |
- 实际上,由于每个更改都反映在$digest阶段,因此可以安全地假设$observe回调将在$digest中调用。在$digest中也会调用$watch回调,但只要值发生变化。我认为它们做的工作完全相同:"观察表达式,调用回调值更改"。关键字差异可能只是为了不混淆开发人员的语法糖分。
- @FastReload,我完全同意你的意见。写得很好!
- @谢谢你的精彩解释。如果我理解正确的话,观察者是指角表达式。我说的对吗?
- @Primosk:为我之前的评论添加你。
- @abilash观察器用于监视dom属性,而不仅仅是表达式。因此,如果您自己更改属性值,它将反映在下一个摘要循环中。
- @快装:太好了。非常感谢你!
我认为这很明显:
- $observe用于链接指令的函数。
- $watch用于监视作用域值的任何变化。
记住:两个函数都有两个参数,
1
| $observe/$watch(value : string, callback : function); |
- 值:始终是对被监视元素的字符串引用(作用域变量的名称或要监视的指令属性的名称)
- 回调:执行function (oldValue, newValue)形式的函数。
我做了一个PLUNKER,所以您实际上可以了解它们的使用情况。我用变色龙作类比,以便更容易描绘。
- 它的用法很明显。但问题是为什么。马克总结得很好。
- 我认为参数可能会被切换-它似乎传递newValue,然后将oldValue传递给attrs.$observe()。…
为什么$observe与$watch不同?
在每个digest()循环中,将对watchExpression进行计算并与前一个值进行比较,如果watchExpression值发生变化,则调用watch函数。
$观察特定于观察插值值。如果指令的属性值被插入,例如dir-attr="{{ scopeVar }}",则只有在设置了插入值时(因此当$digest已经确定需要进行更新时),才会调用观察函数。基本上,已经有了一个观察插值的人,$observer函数将其作为基础。
请参见compile.js中的$observer&;$set