如何在AngularJS中使用$scope.$watch和$scope.$apply?
在AngularJS中,我们更新我们的模型,然后我们的视图/模板“自动”(通过内置或自定义指令)更新DOM。\n$apply和$watch是Scope方法,与DOM无关。\n“概念”页(部分“运行时”)有一个$digest循环,$apply,$evalAsync队列和$watch列表的相当好的解释。这是伴随文本的图片:\n任何访问作用域的代码 - 通常是控制器和指令(它们的链接函数和/或它们的控制器) - 都可以设置一个“watchExpression”,AngularJS将对该作用域进行评估。此评估发生每当AngularJS进入其$digest循环时(特别是“$watch list”循环)。您可以监视个别作用域属性,可以定义一个函数来一起监视两个属性,可以监视数组的长度等。\n当事情发生“在AngularJS内部”时 - 例如,您键入启用了AngularJS双向数据绑定(即使用ng-model)的文本框,$http回调被触发等 - $apply已经被调用,因此我们位于上图中的“AngularJS”矩形中。所有watchExpression将被评估(可能不止一次 - 直到不再检测到更改)。\n当事情发生“在AngularJS外部”时 - 例如,您在指令中使用了绑定()然后事件发生,导致调用您的回调,或某个jQuery注册的回调被触发 - 我们仍在“本机”矩形中。如果回调代码修改任何$watch正在观察的内容,请调用$apply以进入AngularJS矩形,从而使$digest循环运行,因此AngularJS将注意到更改并执行其魔法。
你需要了解AngularJS的工作原理才能理解它。
循环与$scope
首先,AngularJS定义了一个所谓的循环(digest cycle)的概念。这个循环可以被看作是一个循环,在这个循环中AngularJS检查所有被所有$scope
所监视的变量是否有任何变化。因此,如果你在控制器中定义了$scope.myVar
,并且这个变量被标记为被监视,那么你正在暗示AngularJS在每次循环迭代中监视myVar
的变化。
一个自然的后续问题是:所有附加到$scope
上的东西都会被监视吗?幸运的是,不会。如果您要监视$scope
中的每个对象的变化,那么循环将需要花费大量时间进行评估,您将很快遇到性能问题。这就是为什么AngularJS团队给了我们两种方法来声明一些$scope
变量作为被监视的方式(见下文)。
$watch帮助监听$scope的变化
有两种方法可以声明$scope
变量作为被监视的方式。
- 通过在表达式
{{myVar}}
中在您的模板中使用它 - 通过手动添加
$watch
服务
广告1)
这是最常见的情况,我相信你以前见过它,但你不知道这已经在后台创建了一个监视器。是的,它有!使用AngularJS指令(例如ng-repeat
)也可以创建隐式监视器。
广告 2)
以下是如何创建自己的 手表。 $watch
服务可在附加到 $scope
的值发生更改时运行一些代码。虽然很少使用,但有时很有用。例如,如果要在每次“myVar”更改时运行一些代码,则可以执行以下操作:
function MyController($scope) { $scope.myVar = 1; $scope.$watch('myVar', function() { alert('hey, myVar has changed!'); }); $scope.buttonClicked = function() { $scope.myVar = 2; // This will trigger $watch expression to kick in }; }
$apply 使更改与循环中的摘要集成
您可以将$apply
函数视为集成机制。您看,每次直接更改附加到 $scope
的观察变量时,AngularJS 都会知道已发生更改。这是因为 AngularJS 已知要监视这些更改。因此,如果它发生在由框架管理的代码中,则摘要循环将继续。
但是,有时您想要在 AngularJS 外更改某些值,并且希望看到更改正常传播。
假设您有一个 $scope.myVar
值,它将在 jQuery 的 $.ajax()
处理程序内修改。这将发生在未来的某个时刻。AngularJS 不能等待此操作完成,因为尚未指示它等待 jQuery。
为了应对这种情况,引入了 $apply
。它允许您显式启动摘要循环。但是,您只应将此方法用于将某些数据迁移到 AngularJS(与其他框架的集成),但不能将此方法与常规 AngularJS 代码组合使用,因为那样 AngularJS 将报错。
所有这些与 DOM 有什么关系?
嗯,既然您已经了解了这些,那么您应该重新关注一下教程。摘要循环将确保 UI 和 JavaScript 代码保持同步,通过评估附加到所有 $scope
的 watcher,只要没有更改发生。如果在摘要循环中不再发生更改,则视为已完成。
你可以通过在控制器中显式地添加对象或直接在视图中以{{expression}}
的形式声明的方式将对象附加到$scope
对象上。
进一步阅读: