序文
AngularJSは開発が簡単で、多くの機能と良い効果があり、より多くのアプリケーションをもたらし、いくつかのトラップに伴います。この記事には、AngularJSが問題になりやすい一般的な問題がリストされています。一緒に見てみましょう。
1。MVCディレクトリ構造
Angularjsは、鈍いことで、MVCフレームワークです。そのモデルは、backbone.jsフレームワークほど明確に定義されていませんが、そのアーキテクチャは適切です。 MVCフレームワークで作業する場合、ファイルタイプで分類することが一般的です。
Templates/ _login.html _feed.htmlapp/ app.js Controllers/ logincontroller.js feedcontroller.js Directives/ FeedentryDirective.js Services/ loginservice.js Feedservice.jsフィルター/ CapatalizeFilter.js
これは明らかな構造であるように思われます。言うまでもなく、Railsは同じことをしていることは言うまでもありません。ただし、アプリが拡大し始めると、この構造により多くのディレクトリを一度にオープンさせます。 Sublime、Visual Studio、またはVimをオタクツリーと組み合わせて使用している場合でも、ディレクトリツリーで常に多くの時間を滑り込ませます。
ファイルをタイプで除算するのとは異なり、代わりにファイルを特性で分割できます。
app/ app.js feed/ _feed.html feedcontroller.js feedentrydirective.js feedservice.js login/ _login.html logincontroller.js loginservice.js共有/ capatalizefilter.js
このディレクトリ構造により、機能に関連するすべてのファイルを見つけやすく、開発の進行状況を高速化できます。 .htmlおよび.jsファイルを1か所に配置することは物議を醸すかもしれませんが、節約された時間はより価値があります。
2。モジュール
すべてをメインモジュールの下に置くことは非常に一般的です。小さなアプリの場合、最初は問題はありませんが、すぐに何かが不正行為をしていることがわかります。
var app = angular.module( 'app'、[]); app.service( 'myservice'、function(){// service code}); app.controller( 'myctrl'、function($ scope、myservice){//コントローラーコード});この後、一般的な戦略は、同じタイプのオブジェクトを分類することです。
var services = angular.module( 'services'、[]); services.service( 'myservice'、function(){// service code}); var Controllers = Angular.Module( 'Controllers'、['Services']); controllers.controller( 'myctrl'、function($ scope、myservice){//コントローラーコード}); var app = angular.module( 'app'、['controllers'、 'services']);この方法は、上記の最初の部分に記載されているディレクトリ構造に似ています。十分ではありません。同じ哲学によれば、それは特性によって分類され、スケーラビリティにつながります。
var sharedservicesmodule = angular.module( 'sharedservices'、[]); sharedservices.service( 'networkservice'、function($ http){}); var loginmodule = angular.module( 'login'、['sharedservices']); loginmodule.service( 'loginservice'、function(networkservice){}); loginmodule.controller( 'loginctrl'、function($ scope、loginservice){}); var app = angular.module( 'app'、['sharedservices'、 'login']);大規模なアプリケーションを開発するとき、それは単一のページに含まれるすべてではないかもしれません。同じタイプの機能を1つのモジュールに配置すると、アプリ全体のモジュールを簡単に再利用できます。
3。依存関係注射
依存関係噴射は、AngularJSの最良のパターンの1つであり、テストをより簡単にし、指定されたオブジェクトに依存することについて明確です。 AngularJSの注入方法は非常に柔軟です。最も簡単な方法は、依存関係名をモジュールのfunctionに渡すだけです。
var app = angular.module( 'app'、[]); app.Controller( 'mainctrl'、function($ scope、$ timeout){$ timeout(function(){console.log($ scope);}、1000);});ここでは、MainCtrlが$scopeと$timeoutに依存していることは明らかです。
それを生産に展開する準備ができていて、コードを合理化するまで、すべてが美しいです。 uglifyjsを使用する場合、前の例は次のようになります。
var app = angular.module( "app"、[]); app.controller( "mainctrl"、function(e、t){t(function(){console.log(e)}、1e3)}))Angularjsは、MainCtrlが誰に依存しているかをどのようにして知っていますか? AngularJSは非常に単純な回避策を提供します。つまり、依存関係を配列に渡すこと、アレイの最後の要素は関数であり、すべての依存関係がパラメーターとして使用されます。
app.Controller( 'mainctrl'、['$ scope'、 '$ timeout'、function($ scope、$ timeout){$ timeout(){console.log($ scope);}、1000);}]);そうすることでコードが簡素化され、Angularjsはこれらの明示的な依存関係を解釈する方法を知っています。
app.Controller( "mainctrl"、["$ scope"、 "$ timeout"、function(e、t){t(function(){console.log(e)}、1e3)}))))3.1グローバル依存関係
これは、AngularJSプログラムを作成するときにしばしば発生します。オブジェクトには依存関係があり、このオブジェクトはグローバルな範囲に結合します。つまり、この依存関係はAngularJSコードで利用可能であることを意味しますが、依存関係注入モデルを破壊し、特にテストプロセス中にいくつかの問題を引き起こします。
AngularJSを使用すると、これらのグローバル依存関係をモジュールに簡単にカプセル化できるため、AngularJS標準モジュールのように注入できます。
Underscore.jsは、JavaScriptコードを機能スタイルで簡素化する優れたライブラリであり、次の方法でモジュールに変換できます。
var Underscore = Angular.Module( 'Underscore'、[]); Underscore.Factory( '_'、function(){return window._; //アンダースコアは既にページにロードされている必要があります}); var app = angular.module( 'app'、['underscore']); app.Controller( 'mainctrl'、['$ scope'、 '_'、function($ scope、_){init = function(){_.keys($ scope);} init();}]);このアプローチにより、アプリケーションはAngularJS依存性噴射のスタイルで開発を続けることができ、テストフェーズ中にアンダースコアを交換することもできます。
これは些細で不必要に思えるかもしれませんが、コードがStrictを使用している場合(および使用する必要がある)、これが必要です。
IV。コントローラーの拡張
コントローラーはAngularjs肉とジャガイモであり、注意していない場合は、特に最初に、あまりにも多くの論理を追加します。コントローラーは、DOMを操作したり、DOMセレクターを保持したりしないでください。そこで、指示とNG-Modelを使用する必要があります。同様に、コントローラーではなく、サービスにビジネスロジックが存在する必要があります。
データは、すでに$スコープに拘束されていない限り、サービスに保存する必要があります。サービス自体はシングルトンであり、アプリケーションの存続期間中に存在しますが、コントローラーはアプリケーションの状態間で一時的です。データがコントローラーに保存されている場合、再びインスタンス化された場合、どこかからデータを再ゲットする必要があります。データがLocalStorageに保存されている場合でも、検索速度はJavaScript変数よりも桁違いに遅くなります。
AngularJSは、単一の責任原則(SRP)に従うときにうまく機能します。コントローラーがビューとモデルの間のコーディネーターである場合、可能な限り少ないロジックを含める必要があり、これもテストを容易にします。
5。サービスvs工場
ほとんどすべてのAngularJS開発者は、彼が初心者であるときにこれらの名詞に悩まされていますが、それは本当にそれに値しません。
AngularJSソースコードの定義は次のとおりです。
function Factory(name、factoryfn){return provider(name、{$ get:factoryfn}); } function service(name、constructor){return factory(name、['$ injector'、function($ injector){return $ injector.instantiate(constructor);}];}ソースコードから、サービスは単にfactory関数を呼び出すだけで、後者はprovider関数を呼び出すことがわかります。実際、AngularJSは、いくつかの値、定数、装飾の追加providerカプセル化も提供しますが、これは同様の混乱につながることはありません。その文書は非常に明確です。
サービスはfactory関数のみを呼び出すため、違いは何ですか?手がかりは$injector.instantiate :この関数では、 $injector serviceコンストラクターに新しいインスタンスを作成します。
以下は、 serviceとfactory同じことをどのように行うかを示す例です。
var app = angular.module( 'app'、[]); app.service( 'helloworldservice'、function(){this.hello = function(){return "hello world";};}); app.Factory( 'HelloWorldFactory'、function(){return {hello:function(){return "hello world";}}}); helloWorldServiceまたはhelloWorldFactoryコントローラーに注入されると、それらはすべて「Hello World」を返すHelloメソッドを持っています。 serviceのコンストラクターは、宣言されたときに一度インスタンス化され、 factoryオブジェクトは注入されるたびに渡されますが、まだ1つのfactoryインスタンスしかありません。すべてのprovidersはシングルトンです。
同じことができるので、なぜ2つの異なるスタイルが必要なのですか? serviceと比較して、 factory機能を返すことができるため、より柔軟性を提供します。これは、工場が他のオブジェクトを作成できるオブジェクトにすることができるオブジェクト指向プログラミングの工場パターンの概念に対応しています。
app.Factory( 'hellofactory'、function(){return function(name){this.name = name; this.hello = function(){return "hello" + this.name;};};};});以下は、 serviceと2つのfactoryを使用してコントローラーの例です。HelloFactory helloFactory 、新しいオブジェクトが作成されたときにnameの値を設定する関数を返します。
App.Controller( 'Helloctrl'、function($ scope、helloworldservice、helloworldactory、hellofactory){init = function(){helloworldservice.hello(); // 'hello world' helloworldfactory.hello(); // 'hello world' hellofactory( 'leaders'}; init();});あなたが初心者であるとき、サービスを使用することが最善です。
Factory 、多くのプライベートな方法でクラスを設計するときにも役立ちます。
app.Factory( 'privateFactory'、function(){var privatefunc = function(name){return name.split( "")。reverse( ""); // name( ""); // return {hello:function(name){return "hello" + privatefunc(name);}};};};};};この例を使用して、 privateFuncメソッドをprivateFactoryのパブリックAPIにアクセスできないようにすることができます。このパターンはserviceできますが、 factoryでは簡単です。
6.バタランは使用されていません
Batarangは、AngularJSアプリを開発およびテストするための優れたChromeプラグインです。
Batarangは、モデルを閲覧する機能を提供します。これにより、AngularJがどのようにスコープにバインドされるかを観察する機能が提供されます。これは、指示を処理してバインドされた値の範囲を分離するときに非常に役立ちます。
Batarangは依存関係グラフも提供します。これは、テストされていないコードベースにさらされる場合に役立ちます。これは、どのサービスに焦点を当てるべきかを決定できます。
最後に、Batarangはパフォーマンス分析を提供します。 Angularはパッケージとして使用でき、パフォーマンスが優れていますが、カスタム命令と複雑なロジックでいっぱいのアプリケーションではそれほどスムーズではない場合があります。 Batarang Performanceツールを使用すると、どの関数がダイジェストサイクルで最も長い時間を実行するかを直接観察できます。パフォーマンスツールは、完全な時計ツリーを表示することもできます。これは、ウォッチャーが多い場合に役立ちます。
7.ウォッチャーが多すぎます
前のポイントでは、Angularjsはパッケージとして使用できると述べました。ダーティデータチェックは1つのダイジェストサイクルで完了する必要があるため、ウォッチャーの数が約2000に増加すると、このサイクルは重大なパフォーマンスの問題を引き起こします。 (数2000は大幅なパフォーマンスの低下を引き起こすとは言えませんが、これは良い経験的価値です。AngularJS1.3リリースバージョンでは、ダイジェストサイクルの厳密な制御を可能にするいくつかの変更がすでにあります。)
次の「即時実行関数式(IIFE)」は、現在のページのすべてのウォッチャーの数を印刷します。コンソールに貼り付けて、結果を観察するだけです。このiifeは、Stackoverflowに関するJaredの答えから派生しています。
(function(){var root = $(document.getelementsbytagname( 'body')); var watchers = []; var f = function(element){if(element.data()。hasownproperty( '$ scope')){angular.foreach(element.data()。$ scope。 angular.foreach(childerement){f(childelement);});このようにして、Batarangパフォーマンスセクションの時計ツリーと組み合わせて、ウォッチャーの数が取得され、複製コードが存在する場所、または定数データが存在する場所と所有時計を確認する必要があります。
変更されていないデータがあり、AngularJを使用してテンプレートする場合は、Bindonceの使用を検討できます。 Bindonceは、AngularJSでテンプレートを使用できるようにする簡単な指令ですが、時計に追加されないため、時計の数が成長しないようにします。
8。$スコープの限られた範囲
JavaScriptプロトタイプベースの継承は、通常は問題ではありませんが、オブジェクト指向のオブジェクト指向のクラスベースの継承と微妙な違いがありますが、 $scopeを使用するとこの繊細さは現れます。 Angularjsでは、各$scope親$scopeを継承します。これは、最高レベルで$rootScopeと呼ばれます。 ( $scope 、従来の指令とは多少異なります。特定のアクション範囲があり、明示的に宣言されたプロパティのみを継承しています。)
プロトタイプの継承の特性により、親と子のクラス間でデータを共有することは重要ではありませんが、注意が必要ない場合は、親$scopeの財産を誤用するのは簡単です。
たとえば、ログインフォームに入力されるナビゲーションバーにユーザー名を表示する必要があります。次の試みは機能するはずです。
<div ng-controller = "navctrl"> <span> {{user}} </span> <div ng-controller = "loginctrl"> <span> {{user}} </span> <input ng-model = "user"> </input> </div> </div>次に、質問は...:ユーザーのng-modelがテキスト入力に設定されています。ユーザーがコンテンツを入力すると、どのテンプレートが更新されますか? navctrlまたはloginctrl、またはすべて?
LoginCtrlを選択した場合、プロトタイプの継承がどのように機能するかを理解している可能性があります。
リテラルを取得すると、プロトタイプチェーンは機能しません。 NAVCTRLが同時に更新された場合、プロトタイプチェーンを取得する必要があります。しかし、値がオブジェクトの場合、これは起こります。 (JavaScriptでは、関数、配列、およびオブジェクトがすべてオブジェクトであることを忘れないでください)
したがって、予想される動作を取得するには、LoginCtrlで参照できるNAVCTRLにオブジェクトを作成する必要があります。
<div ng-controller = "navctrl"> <span> {{user.name}} </span> <div ng-controller = "loginctrl"> <span> {{user.name}} </span> <input ng-model = "user.name"> </input> </div> </div>これで、ユーザーはオブジェクトであるため、プロトタイプチェーンが機能し、NAVCTRLテンプレートと$scopeとloginCtrlが更新されます。
これはかなり人工的な例のように思えますが、特定の指示を使用してngRepeatなどの子$scopeを作成すると、この問題は簡単に発生する可能性があります。
9。手動テスト
TDDはすべての開発者が好む方法ではないかもしれないので、コードが機能するか他の何かに影響するかを確認するときに手動テストを行います。
AngularJSアプリをテストしないことは意味がありません。 AngularJSは、ゼロからテスト可能にするように設計されており、依存関係の注入とNGMOCKモジュールがこの証拠です。 AngularJSのコアチームは、テストを次のレベルに引き上げることができる多数のツールを開発しました。
9.1分度器
ユニットテストはテスト作業の基礎ですが、アプリのますます複雑になることを考慮すると、統合テストは実際の状況に近づいています。幸いなことに、AngularJSのコアチームは必要なツールを提供しました。
ユーザーインタラクションをシミュレートするエンドツーエンドのテスターであるProtractorを確立しました。これにより、AngularJSプログラムの健康を確認するのに役立ちます。
プロトラクターは、ジャスミンテストフレームワークを使用してテストを定義します。プロトラクターは、異なるページインタラクションの動作に対して非常に堅牢なAPIを持っています。
他にもエンドツーエンドのテストツールがいくつかありますが、長期にわたる利点は、特に$ダイジェストサイクルでAngularJSコードの操作方法を理解していることです。
9.2カルマ
プロトラクターとの統合テストの執筆を完了したら、次のステップはテストを実行することです。テストが実行されるのを待つこと、特に統合テストは、すべての開発者にとってかすかな悲しみです。 Angularjsのコアチームも非常に苦しんでいると感じたため、Karmaを開発しました。
Karmaは、フィードバックループをオフにするのに役立つテスターです。カルマは、指定されたファイルが変更されたときにテストを実行するため、これを行うことができます。 Karmaは複数のブラウザでテストを実行し、異なるデバイスもKarmaサーバーを指すことができ、実際のアプリケーションシナリオをより適切にカバーできます。
10。jqueryを使用します
jQueryはクールなライブラリであり、標準化されたクロスプラットフォーム開発を備えており、近代的なWeb開発にはほとんど必要になりました。しかし、JQueryの非常に多くの優れた特徴にもかかわらず、その哲学はAngularjsと一致していません。
Angularjsはアプリを構築するためのフレームワークですが、jQueryは「HTMLドキュメント操作、イベント処理、アニメーション、AJAX」を簡素化するライブラリです。これは、2つの最も基本的な違いです。 AngularJSはプログラムのアーキテクチャに取り組んでおり、HTMLページとは何の関係もありません。
AngularJSプログラムの構築方法をよりよく理解するために、JQueryの使用を停止してください。 jQueryにより、開発者は既存のHTML標準の問題について考えることができますが、ドキュメントが言うように、「AngularjsはアプリケーションでHTMLという用語を拡張できます」。
DOM操作は指示でのみ行う必要がありますが、それはjQueryでのみカプセル化できるという意味ではありません。 jQueryを使用する前に、この関数が既にAngularJSによって提供されているかどうかを常に考える必要があります。指示が互いに依存している場合、強力なツールを作成することは本当に強力です。
しかし、本当に素晴らしいjQueryは、その日が来るかもしれないときに必要ですが、そもそもそれを紹介することはよくある間違いです。
要約します
AngularJSは、コミュニティの助けを借りて常に改善している優れたフレームワークです。 Angularjsはまだ進化する概念ですが、AngularJSアプリケーションの開発で遭遇する問題を避けるために、人々が上記の条約に従うことができることを願っています。この記事の内容がすべての人に役立つことを願っています。ご質問がある場合は、メッセージを残してコミュニケーションをとることができます。 wulin.comへのご支援ありがとうございます。