数据绑定在 AngularJS 中是如何工作的? [英] How does data binding work in AngularJS?

查看:29
本文介绍了数据绑定在 AngularJS 中是如何工作的?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

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

myobject.myproperty="新值";

解决方案

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

$apply() 方法,即当您从非 AngularJS 世界转换到 AngularJS 世界时调用的方法,它调用 $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 毫秒.如您所见,即使在如今的浏览器速度较慢的情况下,这也不是问题.有一个警告:比较需要简单以适应时间限制......不幸的是,在 AngularJS 中添加慢比较太容易了,所以当你不知道你什么时很容易构建慢应用是做.但我们希望通过提供一个检测模块来得到答案,该模块将向您展示哪些是慢速比较.

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

How does data binding work in the AngularJS framework?

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?

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 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.

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.

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.

Issues with change listeners:

  • 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.

What about performance?

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.

Humans are:

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

  • 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.

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.

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.

这篇关于数据绑定在 AngularJS 中是如何工作的?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆