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

2022-01-29 00:00:00 angularjs javascript data-binding

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

How does data binding work in the AngularJS framework?

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

I haven't found technical details on their site. It's more or less clear how it works when data is propagated from view to model. But how does AngularJS track changes of model properties without setters and getters?

我发现有 JavaScript 观察者可以完成这项工作.但 Internet Explorer 6 和 Internet Explorer 7.那么 AngularJS 是如何知道我更改了例如以下内容并将此更改反映在视图上呢?

I found that there are JavaScript watchers that may do this work. But they are not supported in Internet Explorer 6 and Internet Explorer 7. So how does AngularJS know that I changed for example the following and reflected this change on a view?

myobject.myproperty="new value";

推荐答案

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

AngularJS remembers the value and compares it to a previous value. This is basic dirty-checking. If there is a change in value, then it fires the change event.

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

The $apply() method, which is what you call when you are transitioning from a non-AngularJS world into an AngularJS world, calls $digest(). A digest is just plain old dirty-checking. It works on all browsers and is totally predictable.

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

To contrast dirty-checking (AngularJS) vs change listeners (KnockoutJS and Backbone.js): While dirty-checking may seem simple, and even inefficient (I will address that later), it turns out that it is semantically correct all the time, while change listeners have lots of weird corner cases and need things like dependency tracking to make it more semantically correct. KnockoutJS dependency tracking is a clever feature for a problem which AngularJS does not have.

  • 语法很糟糕,因为浏览器本身并不支持它.是的,有代理,但它们在所有情况下都不是语义正确的,当然旧浏览器上没有代理.底线是脏检查允许您执行 POJO,而 KnockoutJS 和 Backbone.js 强制您从它们的继承类,并通过访问器访问您的数据.
  • 更改合并.假设您有一个项目数组.假设您想将项目添加到数组中,当您循环添加时,每次添加时都会触发更改事件,这会呈现 UI.这对性能非常不利.你想要的是在最后只更新一次 UI.更改事件过于细化.
  • 更改侦听器会立即在 setter 上触发,这是一个问题,因为更改侦听器可以进一步更改数据,从而触发更多更改事件.这很糟糕,因为在您的堆栈中,您可能会同时发生多个更改事件.假设您有两个数组,无论出于何种原因都需要保持同步.您只能添加到其中一个,但每次添加时都会触发一个更改事件,该事件现在对世界的看法不一致.这是一个与线程锁定非常相似的问题,JavaScript 避免了这种问题,因为每个回调都以独占方式执行并完成.更改事件打破了这一点,因为 setter 可能会产生影响深远的后果,这些后果不是有意的和不明显的,这会再次造成线程问题.事实证明,您要做的是延迟侦听器的执行,并保证一次只运行一个侦听器,因此任何代码都可以自由更改数据,并且它知道在这样做时没有其他代码运行.
  • The syntax is atrocious, since browsers do not support it natively. Yes, there are proxies, but they are not semantically correct in all cases, and of course there are no proxies on old browsers. The bottom line is that dirty-checking allows you to do POJO, whereas KnockoutJS and Backbone.js force you to inherit from their classes, and access your data through accessors.
  • Change coalescence. Suppose you have an array of items. Say you want to add items into an array, as you are looping to add, each time you add you are firing events on change, which is rendering the UI. This is very bad for performance. What you want is to update the UI only once, at the end. The change events are too fine-grained.
  • Change listeners fire immediately on a setter, which is a problem, since the change listener can further change data, which fires more change events. This is bad since on your stack you may have several change events happening at once. Suppose you have two arrays which need to be kept in sync for whatever reason. You can only add to one or the other, but each time you add you fire a change event, which now has an inconsistent view of the world. This is a very similar problem to thread locking, which JavaScript avoids since each callback executes exclusively and to completion. Change events break this since setters can have far-reaching consequences which are not intended and non obvious, which creates the thread problem all over again. It turns out that what you want to do is to delay the listener execution, and guarantee, that only one listener runs at a time, hence any code is free to change data, and it knows that no other code runs while it is doing so.

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

So it may seem that we are slow, since dirty-checking is inefficient. This is where we need to look at real numbers rather than just have theoretical arguments, but first let's define some constraints.

人类是:

  • 慢 — 任何快于 50 ms 的速度对于人类来说是无法察觉的,因此可以被视为即时".

  • Slow — Anything faster than 50 ms is imperceptible to humans and thus can be considered as "instant".

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

Limited — You can't really show more than about 2000 pieces of information to a human on a single page. Anything more than that is really bad UI, and humans can't process this anyway.

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

So the real question is this: How many comparisons can you do on a browser in 50 ms? This is a hard question to answer as many factors come into play, but here is a test case: http://jsperf.com/angularjs-digest/6 which creates 10,000 watchers. On a modern browser this takes just under 6 ms. On Internet Explorer 8 it takes about 40 ms. As you can see, this is not an issue even on slow browsers these days. There is a caveat: The comparisons need to be simple to fit into the time limit... Unfortunately it is way too easy to add a slow comparison into AngularJS, so it is easy to build slow applications when you don't know what you are doing. But we hope to have an answer by providing an instrumentation module, which would show you which are the slow comparisons.

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

It turns out that video games and GPUs use the dirty-checking approach, specifically because it is consistent. As long as they get over the monitor refresh rate (typically 50-60 Hz, or every 16.6-20 ms), any performance over that is a waste, so you're better off drawing more stuff, than getting FPS higher.

相关文章