This article mainly introduces the application of $watch, $apply and $digest of AngularJS two-way data binding principle. It has certain reference value. Interested friends can refer to it
Introduction
This article is written for AngularJS newbies. If you already have an in-depth understanding of AngularJS’s two-way data binding, just go and read the source code.
Background
AngularJS developers all want to know how two-way data binding is implemented. There are many terms related to data-binding: $watch, $apply, $digest, dirty-checking, etc. How do they work? Let’s start from the beginning
The two-way data binding of AngularJS is forced by the browser
The browser looks beautiful, but in fact, in the aspect of data interaction Son, due to the browser's "inaction", the browser's data refresh has become a problem. Specifically, the browser can easily listen to an event, such as when the user clicks a button or enters something in the input box. It also provides an API for event callback functions. The event callback function will be executed in the javascript interpreter. but the reverse is not so simple. If the data from the background changes, the browser needs to be notified and refreshed. The browser does not provide such a data interaction mechanism. For developers, this It is an insurmountable obstacle, what should I do? AngularJS appears, which implements two-way data binding well through $scope. The principle behind it is $watch, $apply, $digest, dirty-checking
$watch queue ($watch list)
Literally, watch means to observe. Every time you bind something to the browser, a $watch will be inserted into the $watch queue. Imagine that $watch is something that can detect changes in the model it monitors. For example, you have the following code
User: <input type="text" ng-model="user" /> Password: <input type="password" ng-model="pass" />
There is $scope.user, which is bound to the first input box, and there is $scope.pass, which is bound to the second input box. on an input box; then add two $watches to the $watch list:
Create a controllers.js file with the following code:
app.controller('MainCtrl', function($scope) { $scope.foo = "Foo"; $scope.world = "World"; });
The corresponding html file, the index.html code is as follows :
Hello, {{ World }}
Here, even if two things are added to $scope, only one is bound to the UI, so only one $watch is generated. Look at the following example:
controllers.js
app.controller('MainCtrl', function($scope) { $scope.people = [...]; });
The corresponding html file index.html
<ul> <li ng-repeat="person in people"> {{person.name}} - {{person.age}} </li> </ul>
It seems that multiple $watches are generated. Each person has two (one name, one age), and ng-repeat is a loop, so the total of 10 persons is (2 * 10) 1, which means there are 21 $watches. Therefore, every data bound to the browser will generate a $watch. Yes, when was $watch generated? Let’s first review the loading principle of AngularJS
The loading principle of AngularJS:
The template loading of AngularJS is divided into two stages: compilation and linking. During the linking phase, the AngularJS interpreter will look for each directive and generate each required $watch. By the way, $watch is generated at this stage.
Next, let’s start using $digest
$digest loop
Literally, digest means “digestion”. I feel that this name is weird. It is strangely related to dirty-checking, which literally means "dirty checking". It is better not to translate it. The original author's original intention is definitely not this, it can only be understood but not expressed in words!
$digest is a loop, what is it doing in the loop? $digest is iterating over our $watch. $digest asks $watch one by one - "Hey, has the data you observed changed?"
This traversal is the so-called dirty-checking. Now that all $watches have been checked, we have to ask: Has $watch been updated? If at least one has been updated, the loop will be triggered again until all $watches are unchanged. This ensures that each model will not change again. Remember that if the loop exceeds 10 times, it will throw an exception to avoid an infinite loop. When the $digest loop ends, the DOM changes accordingly.
Look at the code, for example: controllers.js
app.controller('MainCtrl', function() { $scope.name = "Foo"; $scope.changeFoo = function() { $scope.name = "Bar"; } });
The corresponding html file, index.html
{{ name }} <button ng-click="changeFoo()">Change the name</button>
There is only one $watch here, because ng-click does not generate $ watch (the function will not change).
$digest execution process is:
Press the button in the browser;
The browser receives an event , enter the angular context.
The $digest loop begins to execute, querying whether each $watch changes.
Because the $watch monitoring $scope.name reports a change, it will force another $digest cycle.
The new $digest loop does not detect changes. At this time, the browser takes back control and updates the DOM corresponding to the new value of $scope.name.
We can see an obvious shortcoming of AngularJS: every event entering the angular context will execute a $digest loop. Even if you only enter a letter, $digest will traverse the entire page. of all $watch.
$apply’s application
Angular context 是整个Angular的上下文,也可以把它理解为Angular容器,那么,是谁来决定哪些事件可以进入 Angular Context,哪些事件又不能进入呢? 其控制器在 $apply手上。
如果当事件触发时,调用$apply,它会进入angular context,如果没有调用就不会进入。你可能会问:刚才的例子并没有调用$apply,这是怎么回事呢?原来,是Angular背后替你做了。当点击带有ng-click的元素时,事件就会被封装到一个$apply调用中。如果有一个ng-model="foo"的输入框,当输入一个字母 f 时,事件就会这样调用,$apply("foo = 'f';")。
$apply的应用场景
$apply是$scope的一个函数,调用它会强制一次$digest循环。如果当前正在执行$apply循环,则会抛出一个异常。
如果浏览器上数据没有及时刷新,可以通过调用$scope.$apply() 方法,强行刷新一遍。
通过 $watch 监控自己的$scope
<!DOCTYPE html> <html ng-app="demoApp"> <head> <title>test</title> <!-- Vendor libraries --> <script src="lib/jquery-v1.11.1.js"></script> <script src="lib/angular-v1.2.22.js"></script> <script src="lib/angular-route-v1.2.22.js"></script> </head> <body> <p ng-controller="MainCtrl" > <input ng-model="name" /> Name updated: {{updated}} times. </p> <script > var demoApp = angular.module('demoApp',[]); demoApp.controller('MainCtrl', function($scope) { $scope.name = "Angular"; $scope.updated = -1; $scope.$watch('name', function() { $scope.updated++; }); }); </script> </body> </html>
代码说明:
当controller 执行到 $watch时,它会立即调用一次,所以把updated的值设为 -1 。 上输入框中输入字符发生变化时,你会看到 updated 的值随之变化,而且能显示变化的次数。
$watch 检测到的数据变化
小结
我们对 AngularJS的双向数据绑定有了一个初步的认识,对于AngularJS来说,表面上看操作DOM很简单,其实背后有 $watch、$digest 、 $apply 三者在默默地起着作用。这个遍历检查数据是否发生变化的过程,称之为:dirty-checking。 当你了解了这个过程后,你会对它嗤之以鼻,感觉这种方法好low 哦。 确实,如果一个DOM中有 2000- 3000个 watch,页面的渲染速度将会大打折扣。
这个渲染的性能问题怎么解决呢?随着ECMAScript6的到来,Angular 2 通过Object.observe 极大地改善$digest循环的速度。或许,这就是为什么 Angular 团队迫不及待地推出 Angular 2 的原因吧。
上面是我整理给大家的,希望今后会对大家有帮助。
相关文章:
The above is the detailed content of AngularJS two-way data binding principle (detailed tutorial). For more information, please follow other related articles on the PHP Chinese website!