この記事は、AngularJS の双方向データ バインディング原理の $watch、$apply、$digest の応用を主に紹介します。興味のある方は、
はじめに
を参照してください。 AngularJS の初心者。AngularJS の双方向データ バインディングをすでに深く理解している場合は、ソース コードを読んでください。
背景
AngularJS 開発者は皆、双方向データ バインディングがどのように実装されるかを知りたいと考えています。 $watch、$apply、$digest、ダーティ チェックなど、データ バインディングに関連する用語がたくさんあります。それらはどのように機能するのでしょうか?最初から始めましょう
AngularJS の双方向データバインディングはブラウザによって強制されます
ブラウザは美しく見えますが、実際には、データ対話の領域では、ブラウザの「不作為」により、その結果、ブラウザのデータ更新が問題になります。具体的には、ブラウザーは、ユーザーがボタンをクリックしたり、入力ボックスに何かを入力したりするなどのイベントを簡単にリッスンできます。この目的のために、ブラウザーは、イベント コールバック関数が実行される API も提供します。しかし、その逆はそれほど単純ではありません。バックグラウンドのデータが変更された場合、ブラウザにはそのようなデータ対話メカニズムが用意されていません。これは、乗り越えられない障害です。やるべきですか? $scope を介して双方向のデータ バインディングをうまく実装する AngularJS が登場します。その背後にある原理は $watch、$apply、$digest、dirty-checking です
$watch queue ($watch list)
文字通り、watch です。観察するという意味です。 ブラウザに何かをバインドするたびに、$watch が $watch キューに挿入されます。 $watch が、監視しているモデルの変更を検出できるものであると想像してください。たとえば、次のコードがあります
User: <input type="text" ng-model="user" /> Password: <input type="password" ng-model="pass" />
最初の入力ボックスにバインドされている $scope.user と、2 番目の入力ボックスにバインドされている $scope.pass があります。次に、2 つの $watches を追加します。 $watch リストに追加します:
次のコードでcontrollers.js ファイルを作成します:
app.controller('MainCtrl', function($scope) { $scope.foo = "Foo"; $scope.world = "World"; });
対応する HTML ファイル、index.html コードは次のとおりです:
Hello, {{ World }}
ここでは、$scope に追加した場合でも、次の 2 つがありますただし、UI にバインドされているのは 1 つだけなので、$watch は 1 つだけ生成されます。次の例を見てください。
controllers.js
app.controller('MainCtrl', function($scope) { $scope.people = [...]; });
対応する HTML ファイルは、index.html
<ul> <li ng-repeat="person in people"> {{person.name}} - {{person.age}} </li> </ul>
のようになり、複数の $watch が生成されます。が生成されました。 1人につき2つ(名前1つ、年齢1つ)、ng-repeatはループなので、合計10人は(2 * 10) + 1、つまり21個の$watchが存在します。 したがって、ブラウザにバインドされたすべてのデータは $watch を生成します。はい、$watch はいつ生成されましたか? まず AngularJS の読み込み原理を確認しましょう
AngularJS の読み込み原理:
AngularJS テンプレートの読み込みは、コンパイルとリンクの 2 つの段階に分かれています。リンク段階では、AngularJS インタープリターが各ディレクティブを検索し、それぞれを生成します。 $watch が必要です。ちなみに$watchはこの段階で生成されます。
次に、$digest を使い始めましょう
$digest ループ
文字通り、digest は「消化」を意味し、この名前は奇妙に感じますが、文字通り「ダーティ」を意味します。検査」なので、翻訳しない方が良いでしょう。原作者の本来の意図は決してこれではない、理解はできるけど言葉では表現できない!
$digest はループですが、ループ内で何をしているのでしょうか? $digest は $watch を反復処理しています。 $digest は $watch に 1 つずつ尋ねます - 「ねえ、あなたが観察したデータは変更されましたか?
この走査は、いわゆるダーティ チェックです。」すべての $watch がチェックされたので、次は次のように尋ねる必要があります: $watch は更新されましたか?少なくとも 1 つが更新されている場合、すべての $watch が変更されないまでループが再度トリガーされます。これにより、各モデルが再度変更されなくなります。ループが 10 回を超えると、無限ループを避けるために例外がスローされることに注意してください。 $digest ループが終了すると、それに応じて DOM が変更されます。
コードを見てください。例:controllers.js
app.controller('MainCtrl', function() { $scope.name = "Foo"; $scope.changeFoo = function() { $scope.name = "Bar"; } });
対応する HTML ファイル、index.html
{{ name }} <button ng-click="changeFoo()">Change the name</button>
ここには $watch が 1 つだけあります。これは、ng-click が $watch を生成しないためです (関数は変わりません) )。
$digest の実行プロセスは次のとおりです:
ブラウザのボタンを押します。
ブラウザはイベントを受け取り、Angular コンテキストに入ります。
$digest ループが実行を開始し、各 $watch が変更されるかどうかをクエリします。
$scope.name を監視している $watch が変更を報告するため、別の $digest サイクルが強制されます。
この時点で、新しい $digest ループは変更を検出しません。ブラウザは制御を取り戻し、$scope.name の新しい値に対応する DOM を更新します。
このことから、AngularJS の明らかな欠点がわかります。Angular コンテキストに入るすべてのイベントで $digest ループが実行されます。たとえ文字を入力しただけでも、$digest はページ全体のすべての $watch を走査します。
$アプリケーションを適用する
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 的原因吧。
上面是我整理给大家的,希望今后会对大家有帮助。
相关文章:
以上がAngularJS 双方向データ バインディングの原則 (詳細なチュートリアル)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。