我不明白如何使用 $scope.$watch
和 $scope.$apply
. 官方文档没有帮助 .
具体我不明白:
-
他们是否连接到DOM?
-
如何更新模型的DOM更改?
-
它们之间的连接点是什么?
我试过this tutorial,但理解 $watch
和 $apply
是理所当然的 .
$apply
和 $watch
做了什么,以及如何正确使用它们?
我不明白如何使用 $scope.$watch
和 $scope.$apply
. 官方文档没有帮助 .
具体我不明白:
他们是否连接到DOM?
如何更新模型的DOM更改?
它们之间的连接点是什么?
我试过this tutorial,但理解 $watch
和 $apply
是理所当然的 .
$apply
和 $watch
做了什么,以及如何正确使用它们?
7 回答
还有
$watchGroup
和$watchCollection
. 具体来说,如果你想调用一个函数来更新一个在一个不是dom对象的视图中有多个属性的对象,那么$watchGroup
真的很有帮助 . canvas,webGL或服务器请求中的其他视图 . 在这里,文档link .This blog已经涵盖所有创建示例和可理解的解释 .
AngularJS
$scope
函数$watch(), $digest()
和$apply()
是AngularJS中的一些核心函数 . 理解$watch()
,$digest()
和$apply()
对于理解AngularJS至关重要 .当您从视图中的某个位置创建数据绑定到$ scope对象上的变量时,AngularJS会在内部创建"watch" . Watch 意味着AngularJS监视
$scope object
上变量的变化 . 框架是"watching"变量 . Watch 是使用$scope.$watch()
函数创建的,我将在本文后面介绍 .在应用程序的关键点,AngularJS调用
$scope.$digest()
函数 . 此函数遍历所有监视并检查是否有任何监视变量已更改 . 如果监视变量已更改,则调用相应的侦听器函数 . 监听器函数执行它需要做的任何工作,例如更改HTML文本以反映监视变量的新值 . 因此,$digest()
函数触发数据绑定更新 .大多数时候AngularJS会为你调用$ scope . $ watch()和
$scope.$digest()
函数,但在某些情况下你可能需要自己调用它们 . 因此,了解它们的工作方式真的很棒 .$scope.$apply()
函数用于执行某些代码,然后在此之后调用$scope.$digest()
,因此将检查所有监视并调用相应的监听侦听器函数 . 将AngularJS与其他代码集成时,$apply()
函数非常有用 .我将在本文的其余部分详细介绍
$watch(), $digest()
和$apply()
函数 .$ watch()
$scope.watch()
函数创建一个变量的监视 . 注册表时,将两个函数作为参数传递给$watch()
函数:一个值函数
一个监听器功能
这是一个例子:
第一个函数是值函数,第二个函数是监听器函数 .
值函数应返回正在监视的值 . 然后,AngularJS可以根据watch函数上次返回的值检查返回的值 . 这样AngularJS可以确定值是否已更改 . 这是一个例子:
此示例valule函数返回
$scope
变量scope.data.myVar
. 如果此变量的值发生更改,则将返回不同的值,AngularJS将调用侦听器函数 .注意value函数如何将范围作为参数(名称中没有$) . 通过此参数,value函数可以访问
$scope
及其变量 . 如果您需要,值函数也可以查看全局变量,但大多数情况下您会观察$scope
变量 .如果值已更改,则侦听器函数应执行其需要执行的操作 . 也许您需要更改另一个变量的内容,或者设置HTML元素或其他内容 . 这是一个例子:
此示例将HTML元素的内部HTML设置为变量的新值,嵌入在b元素中,使值变为粗体 . 当然,您可以使用代码
{{ data.myVar }
完成此操作,但这只是您在侦听器函数中可以执行的操作的示例 .$ digest()
$scope.$digest()
函数遍历$scope object
中的所有监视及其子$ scope对象(如果有的话) . 当$digest()
遍历 Watch 时,它会调用每个 Watch 的值功能 . 如果value函数返回的值与上次调用时返回的值不同,则调用该监视的监听器函数 .只要AngularJS认为有必要,就会调用
$digest()
函数 . 例如,在执行了按钮单击处理程序之后,或者在AJAX
调用返回之后(在执行了done()/ fail()回调函数之后) .您可能会遇到AngularJS没有为您调用
$digest()
函数的一些极端情况 . 您通常会通过注意数据绑定不更新显示的值来检测到这一点 . 在这种情况下,请调用$scope.$digest()
和它应该管用 . 或者,您可以使用$scope.$apply()
代替我将在下一节中解释 .$ apply()
$scope.$apply()
函数将一个函数作为执行的参数,然后在内部调用$scope.$digest()
. 这使您更容易确保检查所有 Watch ,从而刷新所有数据绑定 . 这是一个$apply()
示例:传递给
$apply()
函数作为参数的函数将更改$scope.data.myVar
的值 . 当函数退出AngularJS时,将调用$scope.$digest()
函数,以便检查所有监视的观察值的变化 .示例
为了说明
$watch()
,$digest(
)和$apply()
如何工作,请看这个例子:他的示例将
$scope.data.time
变量绑定到插值指令,该指令将变量值合并到HTML页面中 . 此绑定在$scope.data.time variable
上内部创建了一个监视 .该示例还包含两个按钮 . 第一个按钮附有一个
ng-click
侦听器 . 单击该按钮时,将调用$scope.updateTime()
函数,然后AngularJS调用$scope.$digest()
以便更新数据绑定 .第二个按钮从控制器函数内部获取一个标准的JavaScript事件监听器 . 单击第二个按钮时,将执行侦听器功能 . 如您所见,两个按钮的侦听器函数几乎相同,但是当调用第二个按钮的侦听器函数时,不会更新数据绑定 . 这是因为在执行第二个按钮的事件侦听器后未调用
$scope.$digest()
. 因此,如果单击第二个按钮,时间将在$scope.data.time
变量中更新,但永远不会显示新时间 .为了解决这个问题,我们可以在按钮事件监听器的最后一行添加
$scope.$digest()
调用,如下所示:而不是在按钮监听器函数内调用
$digest()
,您也可以使用$apply()
函数,如下所示:注意如何从按钮事件侦听器内部调用
$scope.$apply()
函数,以及如何在作为参数传递给$apply()
函数的函数内执行$scope.data.time
变量的更新 . 当$apply()
函数调用在内部完成AngularJS调用$digest()
时,所有数据绑定都会更新 .AngularJS扩展了这个事件循环,创建了一个名为
AngularJS context
的东西 .$watch()
每次在UI中绑定某些内容时,都会在
$watch
列表中插入$watch
.这里我们有
$scope.user
,它绑定到第一个输入,我们有$scope.pass
,它绑定到第二个输入 . 这样做我们将两个$watch
es添加到$watch
列表中 .当我们的模板被加载,AKA处于链接阶段时,编译器将查找每个指令并创建所需的所有
$watch
.AngularJS提供
$watch
,$watchcollection
和$watch(true)
. 下面是一个简洁的图表,解释了从watchers in depth中取得的所有三个 .http://jsfiddle.net/2Lyn0Lkb/
$ digest循环
当浏览器收到可由AngularJS上下文管理的事件时,将触发
$digest
循环 . 该循环由两个较小的循环组成 . 一个处理$evalAsync
队列,另一个处理$watch list
.$digest
将遍历我们拥有的$watch
列表这里我们只有一个
$watch
因为ng-click不会创建任何 Watch .我们按下按钮 .
浏览器接收将进入AngularJS上下文的事件
$digest
循环将运行并将询问每个$ watch是否有变化 .由于正在监视$ scope.name中的更改的
$watch
报告更改,它将强制另一个$digest
循环 .新循环报告没有 .
浏览器获取控制权,它将更新反映$ scope.name新值的DOM
这里重要的是进入AngularJS上下文的每个事件都将运行
$digest
循环 . 这意味着每次我们在输入中写入一个字母时,循环将在此页面中的每个$watch
处运行检查 .$ apply()
如果在触发事件时调用
$apply
,它将通过角度上下文,但如果您不调用它,它将在其外部运行 . 就是这么简单 .$apply
将在内部调用$digest()
循环,它将遍历所有监视以确保使用新更新的值更新DOM .$apply()
方法将触发整个$scope
链上的观察者,而$digest()
方法将仅触发当前$scope
及其children
上的观察者 . 如果没有高级$scope
对象需要了解本地更改,则可以使用$digest()
.刚读完以上所有内容,无聊而困倦(抱歉,但这是真的) . 非常技术性,深入,细致和干燥 . 我为什么要写作?因为AngularJS是庞大的,许多相互关联的概念可以让任何人变得疯狂 . 我经常问自己,我是不是很聪明才能理解它们?没有!这是因为很少有人可以用 for-dummie language 解释所有术语中的技术!好的,让我试试:
1) They are all event-driven things. (我听到了笑声,但请继续阅读)
2) $watch is "on-click".
3) $digest is the boss who checks around tirelessly ,bla-bla-bla但是一个好老板 .
4) $apply gives you the way when you want to do it manually ,就像防故障一样(如果点击不启动,则强制它运行 . )
Now, let's make it visual. Picture this to make it even more easy to grab the idea:
In a restaurant,
经理跑来跑去确保所有服务员都醒着,以应对客户的任何变化 . 这是
$digest()
OWNER有能力根据要求驱动每个人,这是
$apply()
在AngularJS中,我们更新模型,我们的视图/模板“自动”更新DOM(通过内置或自定义指令) .
$ apply和$ watch都是Scope方法,与DOM无关 .
Concepts页面(第"Runtime"节)对$ digest循环,$ apply,$ evalAsync队列和$ watch列表有很好的解释 . 这是文本附带的图片:
无论什么代码都可以访问范围 - 通常是控制器和指令(它们的链接功能和/或它们的控制器) - 可以设置“watchExpression " that AngularJS will evaluate against that scope. This evaluation happens whenever AngularJS enters its $digest loop (in particular, the " $监视列表”循环) . 您可以观看单个范围属性,您可以定义一个函数来一起观看两个属性,您可以观察数组的长度等 .
当事情发生在“AngularJS内部” - 例如,你输入一个启用了AngularJS双向数据绑定的文本框(即使用ng-model),$ http回调触发等等 - $ apply已被调用,所以我们在上图中的“AngularJS”矩形内 . 将评估所有watchExpressions(可能不止一次 - 直到没有检测到进一步的更改) .
当事情发生在“AngularJS之外” - 例如,你在一个指令中使用了bind()然后该事件触发,导致你的回调被调用,或者一些jQuery注册的回调触发 - 我们仍然在“Native”矩形中 . 如果回调代码修改了任何$ watch正在观看的内容,请调用$ apply进入AngularJS矩形,导致$ digest循环运行,因此AngularJS会注意到更改并发挥其魔力 .
您需要了解AngularJS如何工作才能理解它 .
摘要周期和$ scope
首先,AngularJS定义了一个所谓的 digest cycle 的概念 . 这个循环可以被认为是一个循环,在此循环期间,AngularJS检查所有
$scope
对所有变量 watched 是否有任何变化 . 因此,如果您在控制器中定义了$scope.myVar
且此变量为 marked for being watched ,则您隐式告诉AngularJS在循环的每次迭代中监视myVar
上的更改 .一个自然的后续问题是:是否所有人都被关注了
$scope
?幸运的是,没有 . 如果您要监视$scope
中每个对象的更改,那么很快就会需要一段时间来评估摘要循环,您很快就会遇到性能问题 . 这就是为什么AngularJS团队给我们两种方式来声明一些$scope
变量被观察(见下文) .$ watch有助于监听$ scope更改
有两种方法可以将
$scope
变量声明为被监视 .通过表达式
<span>{{myVar}}</span>
在模板中使用它通过
$watch
服务手动添加广告1)这是最常见的情况,我以前看过它,但你不知道这在后台创造了一个 Watch . 是的,它有!使用AngularJS指令(例如
ng-repeat
)也可以创建隐式监视 .广告2)这就是你如何创建自己的 watches .
$watch
服务可帮助您在$scope
附加的某个值发生更改时运行某些代码 . 它很少使用,但有时很有帮助 . 例如,如果要在每次'myVar'更改时运行某些代码,则可以执行以下操作:$ apply可以将更改与摘要周期集成
你可以想到 $apply function as of an integration mechanism . 你看,每次你直接更改一些 watched variable attached to the $scope 对象时,AngularJS都会知道发生了变化 . 这是因为AngularJS已经知道要监控这些变化 . 因此,如果它发生在框架管理的代码中,摘要周期将继续 .
但是,有时您希望 change some value outside of the AngularJS world 并看到更改正常传播 . 考虑一下 - 你有一个
$scope.myVar
值,它将在jQuery的$.ajax()
处理程序中修改 . 这将在未来的某个时刻发生 . AngularJS可以被指示等待jQuery .为了解决这个问题,已经引入了
$apply
. 它可以让您明确地开始消化循环 . 但是,您应该只使用它来将一些数据迁移到AngularJS(与其他框架集成),但是从不将此方法与常规AngularJS代码结合使用,因为AngularJS会抛出错误 .所有这些都与DOM有关吗?
好吧,你应该再次按照教程,既然你知道这一切 . 只要没有任何变化,摘要周期将通过评估附加到所有
$scope
的每个观察者来确保UI和JavaScript代码保持同步 . 如果摘要循环中没有更多的更改,则认为它已完成 .您可以在Controller中显式地将对象附加到
$scope
对象,也可以直接在视图中以{{expression}}
形式声明它们 .我希望这有助于澄清有关这一切的一些基本知识 .
进一步阅读:
我发现了非常深入的视频,涵盖了
$watch
,$apply
,$digest
和摘要周期:AngularJS - Understanding Watcher, $watch, $watchGroup, $watchCollection, ng-change
AngularJS - Understanding digest cycle (digest phase or digest process or digest loop)
AngularJS Tutorial - Understanding $apply and $digest (in depth)
以下是在这些视频中使用的几张幻灯片来解释这些概念(以防万一,如果上述链接被删除/不工作) .
在上图中,"$scope.c"未被监视,因为它未在任何数据绑定中使用(在标记中) . 另外两个(
$scope.a
和$scope.b
)将被观看 .从上图:根据各自的浏览器事件,AngularJS捕获事件,执行摘要周期(通过所有 Watch 进行更改),执行监视功能并更新DOM . 如果不是浏览器事件,则可以使用
$apply
或$digest
手动触发摘要周期 .有关
$apply
和$digest
的更多信息: