数据绑定如何在AngularJS中运行?

本文翻译自:How does data binding work in AngularJS?

How does data binding work in the AngularJS framework? 数据绑定如何在AngularJS框架中工作?

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? 但是AngularJS如何在没有setter和getter的情况下跟踪模型属性的变化?

I found that there are JavaScript watchers that may do this work. 我发现有一些JavaScript观察者可以做这项工作。 But they are not supported in Internet Explorer 6 and Internet Explorer 7 . Internet Explorer 6Internet Explorer 7不支持它们。 So how does AngularJS know that I changed for example the following and reflected this change on a view? 那么AngularJS如何知道我改变了例如以下内容并在视图上反映了这一变化?

myobject.myproperty="new value";

#1楼

参考:https://stackoom.com/question/eckm/数据绑定如何在AngularJS中运行


#2楼

I wondered this myself for a while. 我有一段时间自己想知道这件事。 Without setters how does AngularJS notice changes to the $scope object? 没有setter, AngularJS如何注意$scope对象的变化? Does it poll them? 它会轮询它们吗?

What it actually does is this: Any "normal" place you modify the model was already called from the guts of AngularJS , so it automatically calls $apply for you after your code runs. 它实际上是这样做的:你修改模型的任何“正常”位置都是从AngularJS调用的,所以它会在代码运行后自动调用$apply for you。 Say your controller has a method that's hooked up to ng-click on some element. 假设您的控制器有一个连接到某个元素的ng- ng-click的方法。 Because AngularJS wires the calling of that method together for you, it has a chance to do an $apply in the appropriate place. 因为AngularJS为您调用了该方法,所以它有机会在适当的位置进行$apply Likewise, for expressions that appear right in the views, those are executed by AngularJS so it does the $apply . 同样,对于出现在视图中的表达式,这些表达式由AngularJS执行,因此它执行$apply

When the documentation talks about having to call $apply manually for code outside of AngularJS , it's talking about code which, when run, doesn't stem from AngularJS itself in the call stack. 当文档谈到不得不为AngularJS之外的代码手动调用$apply ,它正在谈论代码,这些代码在运行时并不源于调用堆栈中的AngularJS本身。


#3楼

Misko already gave an excellent description of how the data bindings work, but I would like to add my view on the performance issue with the data binding. Misko已经对数据绑定的工作原理进行了很好的描述,但我想在数据绑定的性能问题上添加我的观点。

As Misko stated, around 2000 bindings are where you start to see problems, but you shouldn't have more than 2000 pieces of information on a page anyway. 正如Misko所说,大约2000个绑定是你开始看到问题的地方,但你不应该在一个页面上有超过2000条信息。 This may be true, but not every data-binding is visible to the user. 这可能是真的,但并非每个数据绑定对用户都是可见的。 Once you start building any sort of widget or data grid with two-way binding you can easily hit 2000 bindings, without having a bad UX. 一旦你开始使用双向绑定构建任何类型的小部件或数据网格,你就可以轻松地命中2000个绑定,而不会有糟糕的用户体验。

Consider, for example, a combo box where you can type text to filter the available options. 例如,考虑一个组合框,您可以在其中键入文本以过滤可用选项。 This sort of control could have ~150 items and still be highly usable. 这种控制可能有大约150个项目,仍然非常有用。 If it has some extra feature (for example a specific class on the currently selected option) you start to get 3-5 bindings per option. 如果它有一些额外的功能(例如当前所选选项上的特定类),则每个选项开始获得3-5个绑定。 Put three of these widgets on a page (eg one to select a country, the other to select a city in the said country, and the third to select a hotel) and you are somewhere between 1000 and 2000 bindings already. 将这些小部件中的三个放在一个页面上(例如,一个用于选择国家/地区,另一个用于选择所述国家/地区的城市,第三个用于选择酒店)并且您已经介于1000到2000个绑定之间。

Or consider a data-grid in a corporate web application. 或者考虑企业Web应用程序中的数据网格。 50 rows per page is not unreasonable, each of which could have 10-20 columns. 每页50行并不合理,每行可以有10-20列。 If you build this with ng-repeats, and/or have information in some cells which uses some bindings, you could be approaching 2000 bindings with this grid alone. 如果使用ng-repeats构建它,和/或在某些使用某些绑定的单元格中包含信息,则可能仅使用此网格接近2000个绑定。

I find this to be a huge problem when working with AngularJS, and the only solution I've been able to find so far is to construct widgets without using two-way binding, instead of using ngOnce, deregistering watchers and similar tricks, or construct directives which build the DOM with jQuery and DOM manipulation. 我发现使用AngularJS时这是一个很大的问题,到目前为止我能找到的唯一解决方案是构建小部件而不使用双向绑定,而不是使用ngOnce,取消注册观察者和类似的技巧,或构造使用jQuery和DOM操作构建DOM的指令。 I feel this defeats the purpose of using Angular in the first place. 我觉得这首先打败了使用Angular的目的。

I would love to hear suggestions on other ways to handle this, but then maybe I should write my own question. 我很想听听有关处理这个问题的其他方法的建议,但也许我应该写自己的问题。 I wanted to put this in a comment, but it turned out to be way too long for that... 我想把它放在评论中,但事实证明这太长了......

TL;DR TL; DR
The data binding can cause performance issues on complex pages. 数据绑定可能会导致复杂页面出现性能问题。


#4楼

It happened that I needed to link a data model of a person with a form, what I did was a direct mapping of the data with the form. 碰巧我需要将一个人的数据模型与一个表单链接起来,我所做的是将数据与表单直接映射。

For example if the model had something like: 例如,如果模型有类似于:

$scope.model.people.name

The control input of the form: 表格的控制输入:

<input type="text" name="namePeople" model="model.people.name">

That way if you modify the value of the object controller, this will be reflected automatically in the view. 这样,如果修改对象控制器的值,这将在视图中自动反映出来。

An example where I passed the model is updated from server data is when you ask for a zip code and zip code based on written loads a list of colonies and cities associated with that view, and by default set the first value with the user. 我通过模型的示例是从服务器数据更新的,当您要求邮政编码和基于书面加载的邮政编码时,与该视图关联的殖民地和城市列表,默认情况下为用户设置第一个值。 And this I worked very well, what does happen, is that angularJS sometimes takes a few seconds to refresh the model, to do this you can put a spinner while displaying the data. 而且我工作得很好,发生了什么,是angularJS有时需要几秒钟来刷新模型,为此你可以在显示数据的同时放置一个微调器。


#5楼

By dirty checking the $scope object 通过脏检查$scope对象

Angular maintains a simple array of watchers in the $scope objects. Angular在$scope对象中维护一个简单的观察者array If you inspect any $scope you will find that it contains an array called $$watchers . 如果你检查任何$scope你会发现它包含一个名为$$watchersarray

Each watcher is an object that contains among other things 每个观察者都是一个包含其他内容的object

  1. An expression which the watcher is monitoring. 观察者正在监视的表达。 This might just be an attribute name, or something more complicated. 这可能只是一个attribute名称,或者更复杂的东西。
  2. A last known value of the expression. 表达式的最后已知值。 This can be checked against the current computed value of the expression. 可以根据表达式的当前计算值来检查。 If the values differ the watcher will trigger the function and mark the $scope as dirty. 如果值不同,观察者将触发该函数并将$scope标记为脏。
  3. A function which will be executed if the watcher is dirty. 观察者脏的时候执行的功能。

How watchers are defined 如何定义观察者

There are many different ways of defining a watcher in AngularJS. 在AngularJS中定义观察者的方法有很多种。

  • You can explicitly $watch an attribute on $scope . 您可以显式$watch $scope上的attribute

     $scope.$watch('person.username', validateUnique); 
  • You can place a {{}} interpolation in your template (a watcher will be created for you on the current $scope ). 您可以在模板中放置{{}}插值(将在当前$scope为您创建观察器)。

     <p>username: {{person.username}}</p> 
  • You can ask a directive such as ng-model to define the watcher for you. 您可以询问诸如ng-model类的指令来为您定义观察器。

     <input ng-model="person.username" /> 

The $digest cycle checks all watchers against their last value $digest循环检查所有观察者的最后一个值

When we interact with AngularJS through the normal channels (ng-model, ng-repeat, etc) a digest cycle will be triggered by the directive. 当我们通过正常通道(ng-model,ng-repeat等)与AngularJS交互时,指令将触发摘要周期。

A digest cycle is a depth-first traversal of $scope and all its children . 摘要周期是$scope及其所有子项深度优先遍历 For each $scope object , we iterate over its $$watchers array and evaluate all the expressions. 对于每个$scope object ,我们遍历其$$watchers array并评估所有表达式。 If the new expression value is different from the last known value, the watcher's function is called. 如果新表达式值与上一个已知值不同,则调用观察者的函数。 This function might recompile part of the DOM, recompute a value on $scope , trigger an AJAX request , anything you need it to do. 此函数可能会重新编译DOM的一部分,重新​​计算$scope的值,触发AJAX request ,以及您需要执行的任何操作。

Every scope is traversed and every watch expression evaluated and checked against the last value. 遍历每个范围,并根据最后一个值评估和检查每个监视表达式。

If a watcher is triggered, the $scope is dirty 如果触发了观察者,则$scope是脏的

If a watcher is triggered, the app knows something has changed, and the $scope is marked as dirty. 如果触发了观察者,则应用程序知道某些内容已更改,并且$scope被标记为脏。

Watcher functions can change other attributes on $scope or on a parent $scope . Watcher函数可以更改$scope或父$scope上的其他属性。 If one $watcher function has been triggered, we can't guarantee that our other $scope s are still clean, and so we execute the entire digest cycle again. 如果触发了一个$watcher函数,我们无法保证我们的其他$scope仍然是干净的,因此我们再次执行整个摘要周期。

This is because AngularJS has two-way binding, so data can be passed back up the $scope tree. 这是因为AngularJS具有双向绑定,因此可以将数据传递回$scope树。 We may change a value on a higher $scope that has already been digested. 我们可能会更改已经消化的更高$scope的值。 Perhaps we change a value on the $rootScope . 也许我们在$rootScope上更改了一个值。

If the $digest is dirty, we execute the entire $digest cycle again 如果$digest是脏的,我们再次执行整个$digest循环

We continually loop through the $digest cycle until either the digest cycle comes up clean (all $watch expressions have the same value as they had in the previous cycle), or we reach the digest limit. 我们不断循环遍历$digest周期,直到摘要周期清理干净(所有$watch表达式具有与上一周期相同的值),或者我们达到摘要限制。 By default, this limit is set at 10. 默认情况下,此限制设置为10。

If we reach the digest limit AngularJS will raise an error in the console: 如果我们达到摘要限制,AngularJS将在控制台中引发错误:

10 $digest() iterations reached. Aborting!

The digest is hard on the machine but easy on the developer 摘要在机器上很难,但开发人员很容易

As you can see, every time something changes in an AngularJS app, AngularJS will check every single watcher in the $scope hierarchy to see how to respond. 如您所见,每当AngularJS应用程序发生变化时,AngularJS将检查$scope层次结构中的每个观察者,以查看如何响应。 For a developer this is a massive productivity boon, as you now need to write almost no wiring code, AngularJS will just notice if a value has changed, and make the rest of the app consistent with the change. 对于开发人员来说,这是一个巨大的生产力,因为您现在需要编写几乎没有布线代码,AngularJS会注意到值是否已更改,并使应用程序的其余部分与更改保持一致。

From the perspective of the machine though this is wildly inefficient and will slow our app down if we create too many watchers. 从机器的角度来看,虽然这种效率非常低,如果我们创造了太多的观察者,它们会减慢我们的应用程序。 Misko has quoted a figure of about 4000 watchers before your app will feel slow on older browsers. Misko引用了大约4000名观众的数字,之后你的应用程序在旧版浏览器上会感觉很慢。

This limit is easy to reach if you ng-repeat over a large JSON array for example. 例如,如果您对大型JSON array进行ng-repeat ,则很容易达到此限制。 You can mitigate against this using features like one-time binding to compile a template without creating watchers. 您可以使用诸如一次性绑定之类的功能来缓解此问题,以便在不创建观察者的情况下编译模板。

How to avoid creating too many watchers 如何避免创建太多的观察者

Each time your user interacts with your app, every single watcher in your app will be evaluated at least once. 每当您的用户与您的应用互动时,您应用中的每位观察者都会至少评估一次。 A big part of optimising an AngularJS app is reducing the number of watchers in your $scope tree. 优化AngularJS应用程序的一个重要部分是减少$scope树中的观察者数量。 One easy way to do this is with one time binding . 一种简单的方法是使用一次时间绑定

If you have data which will rarely change, you can bind it only once using the :: syntax, like so: 如果您的数据很少会发生变化,您只能使用::语法将其绑定一次,如下所示:

<p>{{::person.username}}</p>

or 要么

<p ng-bind="::person.username"></p>

The binding will only be triggered when the containing template is rendered and the data loaded into $scope . 只有在呈现包含模板并将数据加载到$scope时才会触发绑定。

This is especially important when you have an ng-repeat with many items. 当您有许多项目的ng-repeat时,这一点尤为重要。

<div ng-repeat="person in people track by username">
  {{::person.username}}
</div>

#6楼

Here is an example of data binding with AngularJS, using an input field. 以下是使用输入字段与AngularJS进行数据绑定的示例。 I will explain later 我稍后会解释

HTML Code HTML代码

<div ng-app="myApp" ng-controller="myCtrl" class="formInput">
     <input type="text" ng-model="watchInput" Placeholder="type something"/>
     <p>{{watchInput}}</p> 
</div>

AngularJS Code AngularJS代码

myApp = angular.module ("myApp", []);
myApp.controller("myCtrl", ["$scope", function($scope){
  //Your Controller code goes here
}]);

As you can see in the example above, AngularJS uses ng-model to listen and watch what happens on HTML elements, especially on input fields. 正如您在上面的示例中所看到的, AngularJS使用ng-model来监听和观察HTML元素上发生的事情,特别是在input字段上。 When something happens, do something. 当事情发生时,做点什么。 In our case, ng-model is bind to our view, using the mustache notation {{}} . 在我们的例子中, ng-model使用小胡子符号{{}}绑定到我们的视图。 Whatever is typed inside the input field is displayed on the screen instantly. 输入字段内输入的内容将立即显示在屏幕上。 And that's the beauty of data binding, using AngularJS in its simplest form. 这就是数据绑定的优点,使用最简单的AngularJS。

Hope this helps. 希望这可以帮助。

See a working example here on Codepen 请参阅Codepen上的一个工作示例

发布了0 篇原创文章 · 获赞 3 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/p15097962069/article/details/105182303