ChatGPT解决这个技术问题 Extra ChatGPT

AngularJS 中的数据绑定是如何工作的?

数据绑定如何在 AngularJS 框架中工作?

我没有找到关于 their site 的技术细节。当数据从视图传播到模型时,它或多或少地清楚它是如何工作的。但是AngularJS如何在没有setter和getter的情况下跟踪模型属性的变化呢?

我发现有 JavaScript watchers 可以完成这项工作。但 Internet Explorer 6Internet Explorer 7 不支持它们。那么 AngularJS 是如何知道我更改了以下内容并将此更改反映在视图上的呢?

myobject.myproperty="new value";
请注意,从 Angular 1.0.0rc1 开始,您需要指定 ng-model-instant (docs-next.angularjs.org/api/…) 才能立即更新您的模式。否则它将在模糊事件上更新。
Marcello 的链接显然已损坏,因此再次出现:github.com/mhevery/angular.js/blob/master/docs/content/guide/…
@orian,那个链接不好。更新到(我假设)是相同的 - docs.angularjs.org/guide/databinding
对于那些仍在阅读这个问题的人,请注意 Angular 2.0 自 Angular 1.x 以来已经极大地改变了他们进行数据绑定的方式,以便使用 Web 组件并解决以下答案中的许多问题。

P
Peter Mortensen

AngularJS 会记住该值并将其与之前的值进行比较。这是基本的脏检查。如果值发生变化,则触发 change 事件。

当您从非 AngularJS 世界转换到 AngularJS 世界时调用的 $apply() 方法调用 $digest()。摘要只是简单的旧脏检查。它适用于所有浏览器并且完全可以预测。

对比脏检查 (AngularJS) 与更改侦听器(KnockoutJSBackbone.js):虽然脏检查可能看起来很简单,甚至效率低下(我稍后会解决),但事实证明它在语义上是正确的所有时间,而更改侦听器有很多奇怪的极端情况,需要依赖跟踪之类的东西来使其在语义上更正确。 KnockoutJS 依赖跟踪是 AngularJS 所没有的一个聪明的功能。

更改侦听器的问题:

语法很糟糕,因为浏览器本身不支持它。是的,有代理,但它们在所有情况下都不是语义正确的,当然旧浏览器上没有代理。底线是脏检查允许您执行 POJO,而 KnockoutJS 和 Backbone.js 强制您从它们的类继承,并通过访问器访问您的数据。

改变合并。假设您有一个项目数组。假设您想将项目添加到数组中,当您循环添加时,每次添加时都会触发更改事件,这会呈现 UI。这对性能非常不利。你想要的是在最后只更新一次 UI。更改事件的粒度太细了。

更改侦听器立即在 setter 上触发,这是一个问题,因为更改侦听器可以进一步更改数据,从而触发更多更改事件。这很糟糕,因为在您的堆栈中,您可能会同时发生多个更改事件。假设您有两个数组,无论出于何种原因都需要保持同步。您只能添加到其中一个,但每次添加时都会触发一个更改事件,该事件现在对世界的看法不一致。这是一个与线程锁定非常相似的问题,JavaScript 避免了这种问题,因为每个回调都以独占方式执行并完成。更改事件打破了这一点,因为 setter 可能会产生影响深远的后果,这些后果不是有意的和不明显的,这会再次造成线程问题。事实证明,您要做的是延迟侦听器的执行,并保证一次只运行一个侦听器,因此任何代码都可以自由更改数据,并且它知道在这样做时没有其他代码运行.

性能呢?

所以看起来我们很慢,因为脏检查效率低下。这是我们需要查看实数的地方,而不仅仅是理论上的论据,但首先让我们定义一些约束。

人类是:

慢 - 任何快于 50 毫秒的速度对于人类来说是无法察觉的,因此可以被视为“即时”。

有限——你不能在一个页面上向一个人显示超过 2000 条信息。除此之外的任何东西都是非常糟糕的用户界面,人类无论如何也无法处理它。

所以真正的问题是:你可以在 50 毫秒内在浏览器上进行多少次比较?这是一个很难回答的问题,因为有许多因素在起作用,但这里有一个测试用例:http://jsperf.com/angularjs-digest/6 它创建了 10,000 个观察者。在现代浏览器上,这只需不到 6 毫秒。在 Internet Explorer 8 上大约需要 40 ms。如您所见,即使在如今速度较慢的浏览器上,这也不是问题。有一个警告:比较需要简单以适应时间限制......不幸的是,在 AngularJS 中添加慢速比较太容易了,所以当你不知道自己在做什么时,很容易构建慢速应用程序是做。但是我们希望通过提供一个检测模块来得到答案,它会告诉你哪些是慢速比较。

事实证明,视频游戏和 GPU 使用脏检查方法,特别是因为它是一致的。只要他们超过显示器刷新率(通常为 50-60 Hz,或每 16.6-20 毫秒),任何超过此的性能都是浪费,所以你最好画更多的东西,而不是提高 FPS。


@Mark-是的,在KO中,您只需添加 .extend({ throttle: 500 }) 以在最后一个更改事件之后等待 500 毫秒,然后再对其进行操作。
除了“只要他们获得 50 fps,任何超过它的性能都是浪费,因为人眼无法欣赏它,因此你最好画更多的东西,而不是让 fps 更高。”整个答案都很棒。根据您的应用程序,该陈述是完全不正确的。眼睛绝对可以欣赏超过 50 fps 的速度,并且随着 VR 显示的各种问题(阅读 John Carmack 或 Michael Abrash 的最新文章,尤其是后者的 GDC 2013 VR 演讲),50 fps 实际上太慢了。除此之外,您的回答很棒。我只是不希望错误信息传播。
@DavidRivers 我们是 µs 就像在 utorrent 1µs = 0.000001s
该声明可以很容易地反过来说为“脏检查是淘汰赛没有的问题的一个聪明的功能”。 ES6 正在使用 observables,而 Angular 正在摆脱脏检查。现实世界赶上了这个答案,并证明它是错误的。
“任何快于 50 毫秒的东西对人类来说是无法察觉的”是不正确的。在测试中,我们发现我们的客户可以轻松区分 50 毫秒更新延迟 (20fps) 和 16.6 毫秒更新延迟 (60fps)。即使人们没有有意识地记录帧速率,以前一种速度运行的场景的总体“感觉如何”评分也始终较差。
N
Noor A Shuvo

Misko 已经对数据绑定的工作方式进行了很好的描述,但是我想就数据绑定的性能问题添加我的看法。

正如 Misko 所说,大约 2000 个绑定是您开始发现问题的地方,但无论如何,一个页面上的信息不应超过 2000 条。这可能是真的,但并不是每个数据绑定对用户都是可见的。一旦您开始使用双向绑定构建任何类型的小部件或数据网格,您就可以轻松地达到 2000 个绑定,而不会出现糟糕的用户体验。

例如,考虑一个组合框,您可以在其中键入文本以过滤可用选项。这种控件可能有大约 150 个项目,并且仍然非常有用。如果它有一些额外的功能(例如当前选择的选项上的特定类),您开始每个选项获得 3-5 个绑定。将其中三个小部件放在一个页面上(例如,一个用于选择国家,另一个用于选择该国家的城市,第三个用于选择酒店),您的绑定数已经在 1000 到 2000 个之间。

或者考虑企业 Web 应用程序中的数据网格。每页50行并不是不合理的,每页可以有10-20列。如果您使用 ng-repeats 构建它,和/或在某些使用某些绑定的单元格中有信息,那么仅此网格就可能接近 2000 个绑定。

我发现在使用 AngularJS 时这是一个大问题,到目前为止我能找到的唯一解决方案是在不使用双向绑定的情况下构建小部件,而不是使用 ngOnce、取消注册观察者和类似的技巧,或者构造使用 jQuery 和 DOM 操作构建 DOM 的指令。我觉得这首先违背了使用 Angular 的目的。

我很想听听有关处理此问题的其他方法的建议,但也许我应该写自己的问题。我想把它放在评论中,但结果证明它太长了......

TL;DR 数据绑定可能会导致复杂页面的性能问题。


是的,我支持这个。我们应用的主要职责是显示不同实体之间的连接。一个给定的页面可能有 10 个部分。每个部分都有一个表格。每个表都有 2-5 个预输入过滤器。每个表有 2-5 列,每列有 10 行。很快我们就会遇到性能问题,并使用“类似的技巧”选项。
是否可以公平地说 Angular 不仅仅是关于数据绑定,并且某些应用程序可能不希望使用此功能,因为其他人引用的原因正是如此?我认为 DI 和模块化的方法本身就很有价值;拥有神奇的自动绑定很好,但在每个现有的实现中都需要权衡性能。对于大多数 CRUD Web 应用程序来说,Angular 的方式可以说是优越的,而人们试图将其推向极端只是在碰壁。支持另一种事件侦听方法会很好,但是对于单个框架来说,这从根本上来说太复杂了?
Angular 现在有一种方法和一次绑定数据绑定来帮助解决这个问题。此外,它现在为您的转发器源提供索引,这使您可以修改列表而无需为整个内容重建 dom。
@MW。老实说,我认为 bind-once 是核心。但它似乎不是。这只是你在编写自己的指令时可以做的事情,基本上是在不看它们的情况下链接东西。但是有一个 ux mod:github.com/pasvaz/bindonce
对于任何阅读本文的人来说,未来的呼喊:一次性绑定现在是 Angular v1.3 的核心功能,请在此处阅读更多信息:docs.angularjs.org/guide/expression
C
Community

通过脏检查 $scope 对象

Angular 在 $scope 对象中维护一个简单的 array 观察者。如果您检查任何 $scope,您会发现它包含一个名为 $$watchersarray

每个观察者都是一个 object,其中包含

观察者正在监视的表达式。这可能只是一个属性名称,或者更复杂的名称。表达式的最后一个已知值。这可以根据表达式的当前计算值进行检查。如果值不同,观察者将触发该函数并将 $scope 标记为脏。如果观察者脏了将执行的函数。

如何定义观察者

在 AngularJS 中有许多不同的方法来定义观察者。

您可以显式地 $watch 上的 $scope 属性。 $scope.$watch('person.username', validateUnique);

您可以在模板中放置一个 {{}} 插值(将在当前 $scope 上为您创建一个观察者)。

用户名:{{person.username}}

您可以要求 ng-model 等指令为您定义观察者。

$digest 循环检查所有观察者的最后一个值

当我们通过正常渠道(ng-model、ng-repeat 等)与 AngularJS 交互时,指令将触发一个摘要循环。

摘要循环是对 $scope 及其所有子项的深度优先遍历。对于每个 $scope object,我们迭代其 $$watchers array 并评估所有表达式。如果新的表达式值与最后一个已知值不同,则调用观察者的函数。此函数可能会重新编译 DOM 的一部分,重新计算 $scope 上的值,触发 AJAX request,以及您需要它执行的任何操作。

遍历每个作用域,并根据最后一个值评估和检查每个监视表达式。

如果触发了观察者,则 $scope 是脏的

如果触发了观察者,则应用程序知道发生了一些变化,并且 $scope 被标记为脏。

观察者函数可以更改 $scope 或父 $scope 上的其他属性。如果一个 $watcher 函数被触发,我们不能保证我们的其他 $scope 仍然是干净的,所以我们再次执行整个摘要循环。

这是因为 AngularJS 具有双向绑定,因此可以将数据传回 $scope 树。我们可能会更改已消化的较高 $scope 的值。也许我们改变了 $rootScope 的值。

如果 $digest 是脏的,我们再次执行整个 $digest 循环

我们不断地循环 $digest 循环,直到摘要循环干净(所有 $watch 表达式具有与前一个循环相同的值),或者我们达到摘要限制。默认情况下,此限制设置为 10。

如果我们达到摘要限制,AngularJS 将在控制台中引发错误:

10 $digest() iterations reached. Aborting!

摘要在机器上很难,但对开发人员来说很容易

如您所见,每当 AngularJS 应用程序发生变化时,AngularJS 都会检查 $scope 层次结构中的每个观察者以了解如何响应。对于开发人员来说,这是一个巨大的生产力提升,因为您现在几乎不需要编写任何接线代码,AngularJS 只会注意到值是否发生了变化,并使应用程序的其余部分与更改保持一致。

从机器的角度来看,虽然这非常低效,并且如果我们创建太多的观察者会减慢我们的应用程序。 Misko 引用了大约 4000 名观察者的数据,您的应用程序在旧浏览器上会感觉很慢。

例如,如果您 ng-repeat 超过较大的 JSON array,则很容易达到此限制。您可以使用一次性绑定等功能来缓解这种情况,以在不创建观察者的情况下编译模板。

如何避免创建过多的观察者

每次您的用户与您的应用交互时,您应用中的每个观察者都将至少被评估一次。优化 AngularJS 应用程序的一个重要部分是减少 $scope 树中的观察者数量。一种简单的方法是使用一次性绑定

如果您有很少更改的数据,则可以使用 :: 语法将其绑定一次,如下所示:

<p>{{::person.username}}</p>

或者

<p ng-bind="::person.username"></p>

仅当呈现包含模板并将数据加载到 $scope 时才会触发绑定。

当您有一个包含许多项目的 ng-repeat 时,这一点尤其重要。

<div ng-repeat="person in people track by username">
  {{::person.username}}
</div>

谢谢@user2864740 - 虽然米斯科的答案应该是最重要的。他比任何人都更了解这个框架,而且他参与 Stack Overflow 真是太酷了。
我不同意所说的答案应该在顶部;了解某事与为特定问题撰写相关/详细的答复是有区别的。有更好的方法来获得赞誉。反正 ..
我不怀疑这是真的,但问题和答案的答案:)
很好的答案涵盖了脏检查的行为方式及其实际评估的内容,Misko 的回答中不太清楚的一件事。
精湛而详细的答案。 @superluminary,感谢您的回答。此外,在阅读了这个答案之后,我得出一点,我们不能将非幂等表达式添加为被观察的表达式。
M
Merlin

这是我的基本理解。很可能是错的!

通过将函数(返回要监视的事物)传递给 $watch 方法来监视项目。必须在 $apply 方法包装的代码块内对监视项进行更改。在 $apply 结束时,会调用 $digest 方法,该方法会遍历每个 watch 并检查它们自上次运行 $digest 以来是否发生了变化。如果发现任何更改,则再次调用摘要,直到所有更改稳定。

在正常开发中,HTML 中的数据绑定语法会告诉 AngularJS 编译器为您创建监视程序,并且控制器方法已经在 $apply 中运行。所以对于应用程序开发人员来说,这一切都是透明的。


什么时候触发apply方法?
@EliseuMonar 摘要循环由于某些事件或调用 $apply() 而运行,它不是基于计时器定期调用的。请参阅 How does AngularJS's $watch function work?how does the binding and digesting work in AngularJS?
@remi,我不关心 AngularJS 的最新版本。他们是否已经在使用代理或 Object.observe?如果没有,他们仍然处于脏检查时代,它建立一个定时循环来查看模型属性是否发生了变化。
我读过这个摘要最多可以运行十次sitepoint.com/understanding-angulars-apply-digest
R
Raghav Dinesh

我自己想了一会儿。如果没有设置器,AngularJS 如何注意到 $scope 对象的更改?它会轮询他们吗?

它的实际作用是:您修改模型的任何“正常”位置都已从 AngularJS 的内部调用,因此它会在您的代码运行后自动为您调用 $apply。假设您的控制器有一个在某个元素上连接到 ng-click 的方法。因为 AngularJS 为您将该方法的调用连接在一起,所以它有机会在适当的位置执行 $apply。同样,对于出现在视图中的表达式,它们由 AngularJS 执行,所以它也执行 $apply

当文档谈到必须为 AngularJS 之外的代码手动调用 $apply 时,它所讨论的代码在运行时并非源自调用堆栈中的 AngularJS 本身。


N
Nicolas Zozol

用图片解释:

数据绑定需要映射

范围内的引用并不完全是模板中的引用。当您对两个对象进行数据绑定时,您需要第三个对象来监听第一个对象并修改另一个对象。

https://i.stack.imgur.com/zcMvf.png

在这里,当您修改 <input> 时,您触摸 data-ref3。而经典的数据绑定机制将改变data-ref4。那么其他 {{data}} 表达式将如何移动?

事件导致 $digest()

https://i.stack.imgur.com/IYRLT.png

Angular 维护每个绑定的 oldValuenewValue。在每个 Angular 事件 之后,著名的 $digest() 循环将检查 WatchList 以查看是否发生了变化。这些 Angular 事件 已完成 ng-clickng-change$http...只要任何 oldValuenewValue 不同,$digest() 就会循环。

在上图中,会注意到 data-ref1 和 data-ref2 发生了变化。

结论

这有点像鸡蛋和鸡肉。你永远不知道谁开始,但希望它在大多数情况下都能按预期工作。

另一点是您可以轻松理解简单绑定对内存和 CPU 的深度影响。希望台式机足够胖来处理这个问题。手机没那么强。


M
Merlin

显然,没有定期检查 Scope 附加的对象是否有任何变化。并非所有附加到范围的对象都被监视。范围原型维护一个 $$watchersScope 仅在调用 $digest 时迭代此 $$watchers

Angular 为每一个的 $$watchers 添加了一个观察者

{{expression}} — 在您的模板中(以及任何其他有表达式的地方)或当我们定义 ng-model 时。 $scope.$watch('expression/function') — 在您的 JavaScript 中,我们可以附加一个范围对象以供 Angular 观看。

$watch 函数接受三个参数:

第一个是一个观察函数,它只返回对象,或者我们可以只添加一个表达式。第二个是监听器函数,当对象发生变化时将调用它。所有像 DOM 变化这样的事情都将在这个函数中实现。第三个是可选参数,它采用 boolean 。如果它是 true ,则 angular deep 会监视对象,如果它是 false Angular 只是对对象进行引用监视。 $watch 的粗略实现如下所示

Scope.prototype.$watch = function(watchFn, listenerFn) {
   var watcher = {
       watchFn: watchFn,
       listenerFn: listenerFn || function() { },
       last: initWatchVal  // initWatchVal is typically undefined
   };
   this.$$watchers.push(watcher); // pushing the Watcher Object to Watchers  
};

Angular 中有一个有趣的东西叫做 Digest Cycle。 $digest 循环是调用 $scope.$digest() 的结果。假设您通过 ng-click 指令更改处理函数中的 $scope 模型。在这种情况下,AngularJS 通过调用 $digest() 自动触发 $digest 循环。除了 ng-click,还有其他一些内置指令/服务可以让您更改模型(例如 ng-model、$timeout 等)并自动触发 $digest 循环。 $digest 的粗略实现如下所示。

Scope.prototype.$digest = function() {
      var dirty;
      do {
          dirty = this.$$digestOnce();
      } while (dirty);
}
Scope.prototype.$$digestOnce = function() {
   var self = this;
   var newValue, oldValue, dirty;
   _.forEach(this.$$watchers, function(watcher) {
          newValue = watcher.watchFn(self);
          oldValue = watcher.last;   // It just remembers the last value for dirty checking
          if (newValue !== oldValue) { //Dirty checking of References 
   // For Deep checking the object , code of Value     
   // based checking of Object should be implemented here
             watcher.last = newValue;
             watcher.listenerFn(newValue,
                  (oldValue === initWatchVal ? newValue : oldValue),
                   self);
          dirty = true;
          }
     });
   return dirty;
 };

如果我们使用 JavaScript 的 setTimeout() 函数来更新作用域模型,Angular 无法知道你可能会改变什么。在这种情况下,我们有责任手动调用 $apply(),这会触发 $digest 循环。同样,如果您有一个指令设置 DOM 事件侦听器并更改处理函数中的某些模型,则需要调用 $apply() 以确保更改生效。 $apply 的大想法是我们可以执行一些不知道 Angular 的代码,这些代码可能仍然会改变作用域上的东西。如果我们将该代码包装在 $apply 中,它将负责调用 $digest()。 $apply() 的粗略实现。

Scope.prototype.$apply = function(expr) {
       try {
         return this.$eval(expr); //Evaluating code in the context of Scope
       } finally {
         this.$digest();
       }
};

R
Raju

AngularJS 借助三个强大的函数处理数据绑定机制:$watch()$digest()$apply()。大多数时候 AngularJS 会调用 $scope.$watch() 和 $scope.$digest(),但在某些情况下,您可能必须手动调用这些函数来更新新值。

$watch() :-

此函数用于观察 $scope 上变量的变化。它接受三个参数:表达式、监听器和相等对象,其中监听器和相等对象是可选参数。

$digest() -

此函数遍历 $scope 对象中的所有监视及其子 $scope 对象(如果有的话)。当 $digest() 遍历手表时,它会检查表达式的值是否已更改。如果值改变了,AngularJS 用新值和旧值调用监听器。只要 AngularJS 认为有必要,就会调用 $digest() 函数。例如,在单击按钮之后,或在 AJAX 调用之后。在某些情况下,AngularJS 可能不会为您调用 $digest() 函数。在这种情况下,您必须自己调用它。

$apply() -

Angular 只会自动更新那些在 AngularJS 上下文中的模型更改。当您在 Angular 上下文之外的任何模型中进行更改(例如浏览器 DOM 事件、setTimeout、XHR 或第三方库)时,您需要通过手动调用 $apply() 来通知 Angular 更改。当 $apply() 函数调用完成 AngularJS 在内部调用 $digest() 时,所有数据绑定都会更新。


M
Merlin

碰巧我需要将一个人的数据模型与一个表单联系起来,我所做的就是将数据与表单直接映射。

例如,如果模型具有以下内容:

$scope.model.people.name

表单的控制输入:

<input type="text" name="namePeople" model="model.people.name">

这样,如果您修改对象控制器的值,这将自动反映在视图中。

我传递模型从服务器数据更新的一个例子是,当您要求邮政编码和邮政编码基于书面加载与该视图关联的殖民地和城市列表时,默认情况下为用户设置第一个值。我工作得很好,确实发生了,angularJS 有时需要几秒钟来刷新模型,为此您可以在显示数据时放置一个微调器。


我读了这个答案 5 次,但我仍然不明白这里的意思。
S
Shankar Gangadhar

单向数据绑定是一种从数据模型中获取值并将其插入 HTML 元素的方法。无法从视图中更新模型。它用于经典模板系统。这些系统仅在一个方向上绑定数据。 Angular 应用程序中的数据绑定是模型和视图组件之间数据的自动同步。

数据绑定使您可以将模型视为应用程序中的单一事实来源。视图始终是模型的投影。如果模型发生更改,视图会反映更改,反之亦然。


A
AllJs

这是使用输入字段与 AngularJS 进行数据绑定的示例。我稍后会解释

HTML 代码

<div ng-app="myApp" ng-controller="myCtrl" class="formInput">
     <input type="text" ng-model="watchInput" Placeholder="type something"/>
     <p>{{watchInput}}</p> 
</div>

AngularJS 代码

myApp = angular.module ("myApp", []);
myApp.controller("myCtrl", ["$scope", function($scope){
  //Your Controller code goes here
}]);

正如您在上面的示例中所见,AngularJS 使用 ng-model 来监听和观察 HTML 元素上发生的事情,尤其是在 input 字段上。当有事情发生时,做点什么。在我们的例子中,ng-model 使用小胡子符号 {{}} 绑定到我们的视图。在输入字段中输入的任何内容都会立即显示在屏幕上。这就是数据绑定的美妙之处,以最简单的形式使用 AngularJS。

希望这可以帮助。

Codepen 上查看一个工作示例


o
ojus kulkarni

AngularJs 支持双向数据绑定。意味着您可以访问数据 View -> Controller & Controller -> View

对于前。

1)

// If $scope have some value in Controller. 
$scope.name = "Peter";

// HTML
<div> {{ name }} </div>

输出/输出

Peter

您可以在 ng-model 中绑定数据,例如:-
2)

<input ng-model="name" />

<div> {{ name }} </div>

在上面的示例中,无论用户给出什么输入,它都将在 <div> 标记中可见。

如果要将输入从 html 绑定到控制器:- 3)

<form name="myForm" ng-submit="registration()">
   <label> Name </lbel>
   <input ng-model="name" />
</form>

在这里,如果您想在控制器中使用输入 name

$scope.name = {};

$scope.registration = function() {
   console.log("You will get the name here ", $scope.name);
};

ng-model 绑定我们的视图并在表达式 {{ }} 中呈现它。
ng-model 是在视图中显示给用户并与用户交互的数据。
因此很容易绑定数据在 AngularJs 中。


D
Daniel Tabuenca

Angular.js 为我们在视图中创建的每个模型创建一个观察者。每当模型更改时,模型都会附加一个“ng-dirty”类,因此观察者将观察所有具有“ng-dirty”类的模型并在控制器中更新它们的值,反之亦然。


D
Dhana

数据绑定:

什么是数据绑定?

每当用户更改视图中的数据时,范围模型中的更改都会发生更新,反之亦然。

这怎么可能?

简短回答:借助消化循环。

描述:Angular js 在作用域模型上设置观察者,如果模型发生变化,它会触发监听函数。

$scope.$watch('modelVar' , function(newValue,oldValue){

//Dom用新值更新代码

});

那么何时以及如何调用 watcher 函数呢?

Watcher 函数作为摘要循环的一部分被调用。

摘要循环被称为自动触发,作为内置指令/服务(如 ng-model、ng-bind、$timeout、ng-click 等)的 Angular js 的一部分......让您触发摘要循环。

消化循环功能:

$scope.$digest() -> digest cycle against the current scope.
$scope.$apply() -> digest cycle against the parent scope 

$rootScope.$apply()

注意:$apply() 等于 $rootScope.$digest() 这意味着脏检查从根或顶部或父范围开始,一直到角度 js 应用程序中的所有子 $scope。

上述功能也可以在上述版本的浏览器 IE 中使用,只需确保您的应用程序是 angular js 应用程序,这意味着您使用的是 script 标签中引用的 angularjs 框架脚本文件。

谢谢你。