仕事で実際に開発が必要になったため、Angular フレームワークに触れるようになりました。最初の比較から、さまざまな問題や概念に悩まされ打ちのめされましたが、今ではある程度の理解ができたので、自分の理解を簡単に要約する必要があると感じています。多少の欠点はご容赦ください。
1. 双方向データバインディング
現在、業界ではさまざまな MV** フレームワークが普及しており、関連するフレームワークも絶えず登場しており、Angular もその 1 つ (MVVM) です。実際、MV** フレームワークの中心的な課題は、ビュー層をモデルから分離し、コードの結合を減らし、MVC、MVP、および MVVM のすべてが同じ目標を持ってデータとパフォーマンスの分離を達成することです。しかし、それらの違いは、モデルレイヤーをビューに関連付ける方法にあります。
Angular は、ダーティ チェックを通じてデータの双方向バインディングを実装しており、モデル レイヤーとビュー レイヤーでデータがどのように流れるかが問題の鍵となっています。いわゆる双方向バインディングとは、ビューの変更をモデル レイヤーに反映でき、モデル データの変更をビューに反映できることを意味します。では、Angular はどのようにして双方向バインディングを実現するのでしょうか? なぜダーティチェックになるのでしょうか?フロントエンドの元の質問から始めましょう:
html:
<input type="button" value="increase 1" id="J-increase" /> <span id="J-count"></span>
js:
<script> var bindDate = { count: 1, appy: function () { document.querySelector('#J-count').innerHTML = this.count; }, increase: function () { var _this = this; document.querySelector('#J-increase').addEventListener('click', function () { _this.count++; appy(); }, true); }, initialize: function () { // 初始化 this.appy(); // this.increase(); } }; bindDate.initialize(); </script>
上記の例には、2 つのプロセスがあります:
ビュー レイヤーはモデル レイヤーに影響します。ページ上のボタンをクリックすると、データ数が 1 つ増加します
モデルレイヤーはビューレイヤーを反映します: カウントが変更された後、適用関数を通じてビューレイヤーに反映されます
これは、jquery や YUI などのライブラリを使用して以前に実装されていたデータ処理です。ここでの問題は明らかです。
Angular がデータをどのように処理するかを見てみましょう:
最初のステップ。ウォッチャーを追加します。データが変化したときに、どのオブジェクトを検出する必要があるかを最初に登録する必要があります。
// 对angular里面的源码进行了精简 $watch: function(watchExp, listener, objectEquality) { var scope = this, array = scope.$$watchers, watcher = { fn: listener, last: initWatchVal, get: get, exp: watchExp, eq: !!objectEquality }; if (!array) { array = scope.$$watchers = []; } array.unshift(watcher); }
2 番目のステップ: ダーティチェック: 特定のスコープ内のデータが変更された場合、登録された $$watchers = [...]
を走査して検出する必要があります。$digest: function() { while (length--) { watch = watchers[length]; watch.fn(value, lastValue, scope); } }
これはデータの双方向のバインディングを実現します。上記の実装はカスタム イベントに似ていますか?オブザーバー デザイン パターン (パブリッシャー-サブスクライバー) が使用されていることがわかります。
2. 依存関係の注入
Spring フレームワークを使用したことがある学生は、Ioc と AOP が Spring の 2 つの最も重要な概念であること、および Ioc を使用して依存関係 (DI) を挿入できることを知っています。angular が非常に強力なバックエンド色を持っていることは明らかです。
同様に、まず DI を使用せずにオブジェクトの相互依存性を解決する方法を見てみましょう:
function Car() { ... } Car.prototype = { run: function () {...} } function Benz() { var cat = new Car(); } Benz.prototype = { ... }
上記の例では、クラス Benz はクラス Car に依存しており、この依存関係は内部 New を通じて直接解決されます。この欠点は非常に明白であり、コードの結合が高くなり、メンテナンスが困難になります。バックエンド フレームワークは、初期の段階でこの問題を認識していました。Spring は、オブジェクト間の依存関係を XML ファイルに登録しました。その後、COS 側の学生がアノテーションを利用できるようになり、DI の問題がより簡単に解決されました。バックエンドのコードを見てみましょう。
js 言語自体にはアノテーションのメカニズムがありません。では、angular ではどのようにアノテーションを実装するのでしょうか?
1. シミュレーションの注釈
// 注解的模拟 function annotate(fn, strictDi, name) { var $inject; if (!($inject = fn.$inject)) { $inject = []; $inject.push(name); }else if (isArray(fn)) { $inject = fn.slice(0, last); } return $inject; } createInjector.$$annotate = annotate;
2. インジェクションオブジェクトの作成
function createInjector(modulesToLoad, strictDi) { //通过singleton模式创建对象 var providerCache = { $provide: { provider: supportObject(provider), factory: supportObject(factory), service: supportObject(service), value: supportObject(value), constant: supportObject(constant), decorator: decorator } }, instanceCache = {}, instanceInjector = (instanceCache.$injector = createInternalInjector(instanceCache, function(serviceName, caller) { var provider = providerInjector.get(serviceName + providerSuffix, caller); return instanceInjector.invoke(provider.$get, provider, undefined, serviceName); })); return instanceInjector; }
3. インジェクションオブジェクトを取得します
function invoke(fn, self, locals, serviceName) { var args = [], $inject = annotate(fn, strictDi, serviceName); for (...) { key = $inject[i]; // 替换成依赖的对象 args.push( locals && locals.hasOwnProperty(key) ? locals[key] : getService(key, serviceName) ); } if (isArray(fn)) { fn = fn[length]; } return fn.apply(self, args); }
この時点で、バックエンド フレームワークの設計アイデアをたくさん見たことがありますか? PPK が「非フロントエンダーによる非フロントエンダーによるフロントエンド フレームワーク」であるとアノテーションなしでシミュレートしてみてください。エンダーズ"
3.コントローラー通信
実際の開発では、アプリケーション システムが非常に大規模になるため、アプリケーションにコントローラーが 1 つだけ存在することは不可能であるため、この一般的な問題を解決するには、主に 2 つの方法があります。
1. イベントのメカニズム: $rootScope にイベントを登録する これの問題は、$rootScope に登録されるイベントが多すぎることです。これにより、一連の問題が発生します。
//controller1 app.controller('controller1', function ($rootScope) { $rootScope.$on('eventType', function (arg) { ...... }) }) // controller2 app.controller('controller2', function ($rootScope) { $rootScope.$emit('eventType',arg); or $rootScope.$broadcast('eventType',arg); })
2. サービスによる angular の DI 機能を最大限に活用します: サービスがシングルトンであるという機能を使用して、異なるコントローラー間のブリッジとして機能します
// 注册service app.service('Message', function () { return { count: void(0); } }) // controller1,修改service的count值 app.controller('controller1', function ($scope, Message) { $scope.count = 1; Message.count = $scope.count; }); // controller2, 获取service的count值 app.controller('controller2', function ($scope, Message) { $scope.num = Message.count; });
4.Features of service
1. Singleton: Only service in angular can perform DI such as controller and directive. Neither controller nor directive have these functions. Service literally provides some basic functions. The service is not related to the specific business, while the controller and directive are closely related to the specific business, so the uniqueness of the service needs to be ensured.
2. lazy new: angular will first generate the provider of the service, but does not immediately generate the corresponding service. It will only instantiate these services when they are needed. .
3. Provider) classification: provider(), factory, service, value, constant, where provider is the lowest implementation, and other methods are based on it Syntactic sugar (sugar), it should be noted that these services will eventually add the $get method, because the specific service is generated by executing the $get method.
5. Implementation of directive
The compilation of directive includes two stages: compile and link. To put it simply, the compile phase mainly deals with the template DOM. At this time, the scope issue is not involved, that is, no data rendering is performed. For example, the ngRepeate instruction modifies the template through compile. After executing compile, the link function will be returned, overwriting the link defined later. function; link is mainly used for data rendering, which is divided into two links: pre-link and post-link. The order of parsing of these two links is reverse. Post-link parses the internal part first, and then the external part. This has a great impact on the directive. Parsing is safe because directives can also include directives. At the same time, links process the real DOM, which will involve performance issues in DOM operations.
The content covered in this article is not very universal, and there will be corresponding supplements later. I hope everyone can also learn and discuss the angular framework.