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

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

我在他们的网站上没有找到技术细节。当数据从视图传播到模型时,或多或少都清楚它是如何工作的。但是AngularJS如何在没有setters和getters的情况下跟踪模型属性的变化?

我发现有一些JavaScript watchers可以完成这项工作。但它们在Internet Explorer 6Internet Explorer 7中不支持。那么,AngularJS如何知道我改变了例如以下内容,并将这种改变反映在视图上?

myobject.myproperty="new value";
解决办法

AngularJS会记住这个值并将其与之前的值进行比较。这就是基本的脏检查。如果值有变化,那么它就会触发变化事件。

$apply()方法,也就是你从非AngularJS世界过渡到AngularJS世界时调用的,调用$digest()。文摘只是普通的老式脏检查。它在所有的浏览器上都能工作,并且是完全可预测的。

对比一下dirty-checking(AngularJS)和change listeners(KnockoutJSBackbone.js)。虽然dirty-checking看起来很简单,甚至是低效的(我将在后面讨论这个问题),但事实证明,它在语义上一直是正确的,而change listeners有很多奇怪的角落情况,需要像依赖性跟踪这样的东西来使它在语义上更加正确。KnockoutJS的依赖性跟踪是一个聪明的功能,用于解决AngularJS所没有的问题。

变化监听器的问题。

  • 语法是很糟糕的,因为浏览器不支持它。是的,有代理,但它们并非在所有情况下都是语义正确的,当然,在旧的浏览器上也没有代理。底线是,dirty-checking允许你做POJO,而KnockoutJS和Backbone.js强迫你从他们的类中继承,并通过访问器访问你的数据。
  • 改变凝聚力。假设你有一个项的数组。假设你想把项目添加到数组中,当你在循环添加时,每次添加你都会在变化时触发事件,这就是渲染UI。这对性能非常不利。你想要的是只更新一次用户界面,在最后。变更事件的粒度太细了。
  • 变更监听器在setter上立即触发,这是一个问题,因为变更监听器可以进一步改变数据,这就触发了更多的变更事件。这是不好的,因为在你的堆栈中,你可能有几个变化事件同时发生。假设你有两个数组,由于某种原因需要保持同步。你只能向一个或另一个添加,但每次添加都会触发一个改变事件,现在它对世界的看法是不一致的。这是一个非常类似于线程锁的问题,JavaScript避免了这一问题,因为每个回调都是专门执行的,而且是完成的。变更事件打破了这一点,因为设置器可能会产生深远的影响,而这些影响并不是故意的,也不是显而易见的,这就再次产生了线程问题。事实证明,你要做的是延迟监听器的执行,并保证每次只有一个监听器在运行,因此任何代码都可以自由地改变数据,而且它知道在它这样做时没有其他代码在运行。

性能如何?

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

人类是。

* - 任何超过50 ms的速度对人类来说都是无法察觉的,因此可以被认为是 "即时 "的。

有限 - 你不能在一个页面上向人类展示超过2000条的信息。任何超过这个数量的信息都是非常糟糕的用户界面,而且人类无论如何都无法处理这些信息。

因此,真正的问题是这样的。在浏览器上,你能在50 ms内进行多少次比较?这是一个很难回答的问题,因为有很多因素在起作用,但这里有一个测试案例:http://jsperf.com/angularjs-digest/6,它创建了10,000个观察者。在一个现代浏览器上,这只需要不到6 ms。在Internet Explorer 8上,需要大约40 ms。正如你所看到的,即使在现在的慢速浏览器上,这也不是一个问题。有一个注意事项:比较需要简单,以适应时间的限制......不幸的是,在AngularJS中添加一个慢速比较是太容易了,所以当你不知道你在做什么的时候,很容易建立慢速应用。但我们希望通过提供一个工具模块来解决这个问题,它将显示哪些是慢速比较。

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

评论(38)

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

1.项目是通过传递一个函数(返回要观察的东西)给$watch方法来观察的。 观察)到$watch方法。 2.2. 对被监视项目的改变必须在一个代码块中进行 由$apply'方法包装。 3.3. 在"$apply "结束时,"$digest "方法被调用。 在$apply'结束时,调用`$digest'方法,该方法检查每一个手表,并检查它们是否自 上次"$digest "运行后,它们是否发生了变化。 4.4. 如果发现任何变化,则再次调用digest,直到所有变化稳定下来。

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

评论(4)

我自己也想过这个问题,有一段时间了。如果没有设置器,AngularJS如何注意到$scope对象的变化?它是否对它们进行轮询?

实际上,它是这样做的。你修改模型的任何 "正常 "地方已经从AngularJS的内部调用了,所以它在你的代码运行后自动为你调用$apply。假设你的控制器有一个方法与某个元素上的ng-click'挂钩。因为AngularJS为你连接了该方法的调用,它有机会在适当的地方进行$apply'。同样,对于直接出现在视图中的表达式,这些表达式会被AngularJS执行,所以它可以做$apply

当文档中提到必须手动调用$apply来处理AngularJS'以外的代码时,它是指那些在运行时并不来自AngularJS'本身的调用栈的代码。

评论(0)