前書き
私は最近 Angularjs を使っています。Knockout と比較すると、Angularjs の MVVM フレームワークはより強力で複雑です。しかし、実際にプロジェクトで使用すると、さまざまなチュートリアルがインターネット上に溢れています。 、さまざまな落とし穴に遭遇します。
1. ng-repeat
ng-repeatは、要素を繰り返し出力する必要があることを識別するために使用され、繰り返し出力される内容は一意である必要があります
<div ng-app="app" ng-controller="control"> <h3 ng-repeat="content in repeatContent">ng-repeat: {{ content }}</h3> </div>
let app = angular.module("app", []); app.controller("control", ($scope) => { // 输出李滨泓 $scope.repeatContent = ["李", "滨", "泓"]; // 下面存在两个“泓”,会报错 // $scope.repeatContent = ["李", "滨", "泓", "泓"]; })
2. プロバイダ間、サービス、ファクトリー
ファクトリー
ファクトリーの関係はサービスと非常に似ています。違いは、サービスを使用する必要がある場合、サービスを作成するために new キーワードを使用することです。 1 つだけ)サービス。 Factory は、必要なときに、関数関数のコレクション オブジェクトを返すなど、さまざまな形式のデータを返すメソッドです。
定義:
let app = angular.module("app", []); // 这里可以注入 $http 等 Provider app.factory("Today", () => { let date = new Date(); return { year: date.getFullYear(), month: date.getMonth() + 1, day: date.getDate() }; });
使用インジェクション:
service
service は、使用時にはシングルトン オブジェクトであり、その特性により、何も返さないことができます。 new キーワードを使用して作成されますが、コントローラー間の通信やデータ対話にも使用できます。これは、コントローラーのスコープ チェーンが役に立たない場合 (たとえば、ルートを使用して別のページにジャンプし、別のページを使用して) 破棄されるためです。コントローラー)
定義:
app.controller("control", (Today) => { console.log(Today.year); console.log(Today.month); console.log(Today.day); });
使用インジェクション:
let app = angular.module("app", []); // 这里可以注入 $http 等 Provider // 注意这里不可以使用 arrow function // arrow function 不能作为 constructor app.service("Today", function() { let date = new Date(); this.year = date.getFullYear(); this.month = date.getMonth() + 1; this.day = date.getDate(); });
プロバイダー
プロバイダーはサービスの構成可能なバージョンであると理解できます。プロバイダを提供する前に、プロバイダのいくつかのパラメータを設定します。
定義:
app.controller("control", (Today) => { console.log(Today.year); console.log(Today.month); console.log(Today.day); });
インジェクションの使用:
let app = angular.module("app", []); // 这里可以注入 $http 等 Provider // 注意这里不可以使用 arrow function // arrow function 不能作为 constructor app.provider("Today", function() { this.date = new Date(); let self = this; this.setDate = (year, month, day) => { this.date = new Date(year, month - 1, day); } this.$get = () => { return { year: this.date.getFullYear(), month: this.date.getMonth() + 1, day: this.date.getDate() }; }; });
3. ハンドルバーが角度シンボル解決と競合する
シナリオ:
サーバーとしてnode.jsを使用する場合、中古ハンドルバーはnode.js が応答して URL をレンダリングするとき、そのテンプレートは変数解析シンボルとして { {} } を使用します。同様に、angular も変数解決シンボルとして { {} } を使用するため、node.js がページをレンダリングするときに、{ {} } 内の変数が存在しない場合はその領域がクリアされ、本来の意図はこれです。同時に、ハンドルバーの代わりに angular の解析にも使用したいので、現時点では angular のデフォルトの { {} } 解析シンボルを再定義する必要があります。つまり、次の例に示すように、依存関係の注入 $interpolateProvider を使用して定義します。
// 这里重新配置了今天的日期是 2015年2月15日 // 注意这里注入的是 TodayProvider,使用驼峰命名来注入正确的需要配置的 provider app.config((TodayProvider) => { TodayProvider.setDate(2015, 2, 15); }); app.controller("control", (Today) => { console.log(Today.year); console.log(Today.month); console.log(Today.day); });
IV. ng-annotate-loader
ng-annotate-loader は、次の開発シナリオに適用されます。 webpack + angular であり、進行中の angular の問題を解決するために使用されます。 JS 圧縮後の依存関係注入の失敗とエラーの解決策
インストール
app.config($interpolateProvider => { $interpolateProvider.startSymbol('{[{'); $interpolateProvider.endSymbol('}]}'); });
設定
$ npm install ng-annotate-loader --save-dev
5. 双方向データバインディング
Angular によって提供されていないイベントを使用する場合、 $scope のデータ変更によって $digest のダーティ チェック サイクルが発生しません。これにより、現時点では、モデルが変更されたときにビューが同期的に更新されなくなります。 、更新を自分で積極的にトリガーする必要があります
HTML
// webpack.config.js { test: /\.js?$/, exclude: /(node_modules|bower_components)/, loader: 'ng-annotate!babel?presets=es2015' },
JavaScript
<div>{{ foo }}</div> <button id="addBtn">go</button>
明らかに、この例の意図は、ボタンがクリックされると foo が成長してビューを更新することです。ただし、実際には、$scope.foo は変更されますが、ビューは更新されません。これは、foo には変更を検出した後に $apply を実行するための $watch がなく、最終的に $digest が発生するためです。そのため、自分で $apply をトリガーするか、$apply をトリガーする必要があります。データ変更をトリガーまたは検出するための $watch を作成します
JavaScript ($apply を使用)
app.controller("control", ($scope) => { $scope.foo = 0; document.getElementById("addBtn").addEventListener("click", () => { $scope.foo++; }, false); })
JavaScript ($watch と $digest を使用)
app.controller("control", ($scope) => { $scope.foo = 0; document.getElementById("addBtn").addEventListener("click", () => { $scope.$apply(function() { $scope.foo++; }); }, false); })
6. $watch(watchExpression,listener, [objectEquality])
リスナーのコールバック関数を登録し、watchExpressionの値が変わるたびに
watchExpressionを呼び出す $digestが実行されるたびに呼び出され、検出対象の値を返す(同じ値が複数回入力された場合、watchExpressionは自身の値を変更しないでください。そうしないと、複数の $digest ループが発生する可能性があります。watchExpression は累乗する必要があるなど) 現在の watchExpression の戻り値が最後の watchExpression の戻り値と一致しない場合、
listener が呼び出されます (厳密に一致するには !== を使用します) objectEquality == true を除き、== の代わりに不整合を判断します)
objectEquality が true の場合、一貫性を決定するために angular.equals が使用され、これを保存するために angular.copy が使用されます。次の比較のためにオブジェクトをコピーします。これは、複雑なオブジェクトの検出が行われることを意味します。パフォーマンスとメモリの問題が発生します
7. $apply([exp])
$apply は、$scope の関数であり、$digest をトリガーするために使用されます。ループ
$apply疑似コード
app.controller("control", ($scope) => { $scope.foo = 0; $scope.flag = 0; $scope.$watch("flag", (newValue, oldValue) => { // 当 $digest 循环检测 flag 时,如果新旧值不一致将调用该函数 $scope.foo = $scope.flag; }); document.getElementById("addBtn").addEventListener("click", () => { $scope.flag++; // 主动触发 $digest 循环 $scope.$digest(); }, false); })
$eval(expr)を使ってexpr式を実行
実行中に例外が発生した場合は$ExceptionHandler(e)を実行
最終的には、結果に関係なく、$digestループが1回実行されます