数据绑定在AngularJS中是如何工作的?更改侦听器的问题:性能如何?

2022-08-29 21:55:21

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

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

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

myobject.myproperty="new value";

答案 1

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

当您从非 AngularJS 世界过渡到 AngularJS 世界时,该方法称为 .摘要只是普通的旧脏检查。它适用于所有浏览器,并且是完全可预测的。$apply()$digest()

为了对比脏检查(AngularJS)与更改侦听器(KnockoutJSBackbone.js):虽然脏检查可能看起来很简单,甚至效率低下(我稍后会解决这个问题),但事实证明它在语义上一直是正确的,而更改侦听器有很多奇怪的角落情况,需要像依赖关系跟踪这样的东西才能使其在语义上更加正确。KnockoutJS依赖跟踪是AngularJS没有的问题的一个聪明的功能。

更改侦听器的问题:

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

性能如何?

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

人类是:

  • - 任何快于50毫秒的东西对人类来说都是不可察觉的,因此可以被认为是“即时的”。

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

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

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


答案 2

Misko已经很好地描述了数据绑定的工作原理,但我想补充一下我对数据绑定性能问题的看法。

正如Misko所说,大约2000个绑定是你开始看到问题的地方,但无论如何,你不应该在一个页面上有超过2000条信息。这可能是真的,但并非每个数据绑定对用户都可见。一旦你开始构建任何类型的小部件或数据网格与双向绑定,你可以很容易地达到2000个绑定,而不会有一个糟糕的UX。

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

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

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

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

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