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

我还没有在他们的网站上找到技术细节。当数据从视图传播到模型时,它是如何工作的,这或多或少是清楚的。但是AngularJS如何在没有setter和getter的情况下跟踪模型财产的更改?

我发现有JavaScript观察者可能会做这项工作。但Internet Explorer 6和Internet Explorer 7不支持它们。那么AngularJS如何知道我更改了例如以下内容,并将此更改反映在视图上?

myobject.myproperty="new value";

当前回答

我自己也有一段时间对此感到奇怪。没有setters,AngularJS如何注意到$scope对象的更改?它会投票吗?

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

当文档谈到必须为AngularJS之外的代码手动调用$apply时,它指的是运行时不源自调用堆栈中AngularJS本身的代码。

其他回答

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

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

对比脏检查(AngularJS)和更改侦听器(KnockoutJS和Backbone.js):虽然脏检查可能看起来很简单,甚至效率很低(我稍后会讨论),但事实证明它始终在语义上是正确的,而更改侦听器有很多奇怪的角落情况,需要依赖项跟踪之类的东西来使其更加语义正确。KnockoutJS依赖跟踪是解决AngularJS没有的问题的一个聪明功能。

更改侦听器的问题:

这种语法非常糟糕,因为浏览器本身不支持它。是的,有代理,但它们在所有情况下都不是语义正确的,当然在旧浏览器上也没有代理。底线是,脏检查允许您执行POJO,而KnockoutJS和Backbone.js强制您从它们的类继承,并通过访问器访问数据。更改合并。假设您有一个项目数组。假设您想将项添加到数组中,因为您正在循环添加,每次添加时都会触发更改事件,这就是呈现UI。这对性能非常不利。您需要的是在最后只更新一次UI。更改事件过于精细。更改侦听器会立即触发setter,这是一个问题,因为更改侦听器可以进一步更改数据,从而触发更多的更改事件。这很糟糕,因为在堆栈上,可能会同时发生多个更改事件。假设您有两个数组,无论出于何种原因,它们都需要保持同步。您只能添加其中一个或另一个,但每次添加时都会引发一个更改事件,这会导致对世界的看法不一致。这是一个非常类似于线程锁定的问题,JavaScript避免了这个问题,因为每个回调都以独占方式执行,直到完成。更改事件打破了这一点,因为设置器可能会产生深远的后果,而这些后果不是故意的,也不是显而易见的,这会再次导致线程问题。事实证明,您想要做的是延迟侦听器的执行,并保证一次只运行一个侦听器,因此任何代码都可以自由更改数据,并且它知道在执行此操作时没有其他代码运行。

性能如何?

因此,我们似乎很慢,因为脏检查效率很低。这就是我们需要研究实数的地方,而不仅仅是理论论证,但首先我们来定义一些约束条件。

人类是:

慢-任何速度超过50毫秒的东西都是人类无法察觉的,因此可以被视为“瞬间”。有限-你不能在一个页面上向人类显示超过2000条信息。除此之外的任何东西都是非常糟糕的UI,人类无论如何都无法处理它。

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

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

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

通过传递函数(返回要watch)转换为$watch方法。必须在代码块内对关注的项目进行更改由$apply方法包装。在$apply结束时,调用$digest方法通过每一块手表和检查,看看它们是否发生了变化上次运行$digest时。如果发现任何更改,则再次调用摘要,直到所有更改稳定下来。

在正常开发中,HTML告诉AngularJS编译器为您创建手表,并且控制器方法已经在$apply中运行。所以对于应用程序开发人员来说,它是完全透明的。

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

下面是使用输入字段与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模型来监听和监视HTML元素,尤其是输入字段上发生的情况。当事情发生时,做点什么。在我们的例子中,ng模型使用八字胡符号{{}}绑定到我们的视图。输入字段中键入的任何内容都会立即显示在屏幕上。这就是数据绑定的美妙之处,以最简单的形式使用AngularJS。

希望这有帮助。

请参阅此处的工作示例代码笔

我自己也有一段时间对此感到奇怪。没有setters,AngularJS如何注意到$scope对象的更改?它会投票吗?

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

当文档谈到必须为AngularJS之外的代码手动调用$apply时,它指的是运行时不源自调用堆栈中AngularJS本身的代码。