この記事では、AngularJSでRequireJSを使用して、依存関係の読み込みを簡素化する方法を説明します。 また、Gruntを使用してRequireJSモジュールを含むファイルを生成する方法についても説明します。
RequireJSの簡単な紹介
RequireJSは、JavaScript依存関係の遅延ロードを支援するJavaScriptライブラリです。 モジュールは、いくつかのRequireJS構文シュガーを持つ通常のJavaScriptファイルです。 RequireJSは、CommonJSで指定された非同期モジュール定義を実装します。 RequireJSは、モジュールを作成してアクセスするためのシンプルなAPIを提供します。
RequireJSには、モジュールや「スペーサー」へのパスなどの基本的な構成データを含むマスターファイルが必要です。 次のスニペットは、main.jsファイルのスケルトンを示しています。
require.config({ map:{ // Maps }, paths:{ // }, shim:{ // } });
パスセクションですべてのアプリケーションモジュールを指定する必要はありません。 相対パスを使用してロードできます。 モジュールを宣言するには、define()ブロックを使用する必要があります。
define([ // ], function( // ){ function myModule() { // , } return myModule; });
モジュールには依存関係がない場合があります。 通常、オブジェクトはモジュールの最後に返されますが、これは必要ありません。
AngularJSでの依存性注入とRequireJSでの依存性管理
AngularJS開発者からよく聞かれる質問の1つは、AngularJSとRequireJSの依存関係管理の違いに関するものです。 ここで、両方のライブラリの目的が完全に異なることを覚えておくことが重要です。 AngularJSに組み込まれている依存性注入システムは、コンポーネントで必要なオブジェクトを処理します。 一方、RequireJSの依存関係管理はモジュールまたはJavaScriptファイルを処理します。
RequireJSがモジュールを読み込もうとすると、最初にすべての依存関係をチェックし、それらを読み込みます。 ロードされたモジュールオブジェクトはキャッシュされ、同じモジュールが再度要求されると提供されます。 一方、AngularJSは、IMETとそれに対応するオブジェクトのリストでインジェクターをサポートします。 オブジェクトは、コンポーネントの作成時にインジェクターに追加され、登録された名前で参照されると提供されます。
RequireJSとAngularJSを一緒に使用する
この記事に含まれているコードは、 ここからダウンロードできますが、単純な2ページのアプリケーションです。 次の外部依存関係があります。
- RequireJS
- jQuery
- Angularjs
- 角ルート
- 角度リソース
- 角度UI ngGrid
これらのファイルは、ここに示す順序でページに直接アップロードする必要があります。 また、必要なAngularJSコンポーネントのコードを含む5つの独自ファイルがあります。 これらのファイルがどのように決定されるかを見てみましょう。
RequireJSモジュールとしてのAngularJSコンポーネントの定義
AngularJSコンポーネントは次のもので構成されます。
- 関数宣言
- 依存性注入
- Angularモジュールへの登録
これらの3つのタスクのうち、最初の2つを別々のモジュール(RequireJS)内で実行し、3つ目のタスクは別のモジュールとして実行され、AngularJSモジュールの作成を担当します。
まず、構成ブロックを定義しましょう。 構成ブロックは他のブロックに依存せず、最終的に構成関数を返します。 ただし、別のモジュール内に構成モジュールをロードする前に、構成ブロックに必要なすべてをロードする必要があります。 次のコードはconfig.jsファイルにあります。
define([],function(){ function config($routeProvider) { $routeProvider.when('/home', { templateUrl: 'templates/home.html', controller: 'ideasHomeController' }) .when('/details/:id',{ templateUrl:'templates/ideaDetails.html', controller:'ideaDetailsController'}) .otherwise({redirectTo: '/home'}); } config.$inject=['$routeProvider']; return config; });
このスニペットで使用されている依存性注入メソッドに注意してください。 上記で宣言された構成関数は単純なJavaScript関数であるため、注入された依存関係を取得するために$ injectを使用しました。 モジュールを閉じる前に、将来使用するために依存モジュールに渡すことができるように、config関数を返します。
このアプローチに従って、他のタイプのAngularJSコンポーネントを定義します。さらに、これらのファイルにはコンポーネント固有のコードがありません。 次のスニペットは、コントローラーの定義を示しています。
define([], function() { function ideasHomeController($scope, ideasDataSvc) { $scope.ideaName = 'Todo List'; $scope.gridOptions = { data: 'ideas', columnDefs: [ {field: 'name', displayName: 'Name'}, {field: 'technologies', displayName: 'Technologies'}, {field: 'platform', displayName: 'Platforms'}, {field: 'status', displayName: 'Status'}, {field: 'devsNeeded', displayName: 'Vacancies'}, {field: 'id', displayName: 'View Details', cellTemplate: '<a ng-href="#/details/{{row.getProperty(col.field)}}">View Details</a>'} ], enableColumnResize: true }; ideasDataSvc.allIdeas().then(function(result){ $scope.ideas=result; }); } ideasHomeController.$inject=['$scope','ideasDataSvc']; return ideasHomeController; });
アプリケーションのAngularモジュールは、これまでに定義された各モジュールに依存します。 このファイルは、他のすべてのファイルからオブジェクトを受け取り、それらをAngularJSモジュールにフックします。 このファイルは、結果として何も返さない場合があります。angular.module()を使用してどこからでも参照できます。 次のスニペットは、Angularモジュールを定義しています。
define(['app/config', 'app/ideasDataSvc', 'app/ideasHomeController', 'app/ideaDetailsController'], function(config, ideasDataSvc, ideasHomeController, ideaDetailsController){ var app = angular.module('ideasApp', ['ngRoute','ngResource','ngGrid']); app.config(config); app.factory('ideasDataSvc',ideasDataSvc); app.controller('ideasHomeController', ideasHomeController); app.controller('ideaDetailsController',ideaDetailsController); });
このAngularアプリケーションは、必要なスクリプトが非同期でロードされるため、ng-appディレクティブを使用して起動できません。 ここでの正しいアプローチは、手動開始を使用することです。 これは、main.jsという特別なファイルで行う必要があります。 ここでは、まずAngularモジュールの定義を含むファイルをロードする必要があります。 このファイルのコードを以下に示します。
require(['app/ideasModule'], function() { angular.bootstrap(document, ['ideasApp']); } );
RequireJSモジュールを結合するためのGruntの構成
大規模なJavaScriptアプリケーションをデプロイするときは、スクリプトファイルを結合して縮小し、ダウンロード速度を最適化する必要があります。 Gruntのようなツールは、これらのタスクを自動化するのに役立ちます。 フロントエンドの展開プロセスを簡単にするために、さまざまなタスクが定義されています。 RequireJSモジュールファイルを正しい順序で結合し、結果のファイルを縮小するgrunt-contrib-requirejsタスクがあります。 他のGruntタスクと同様に、展開の各段階で異なる動作をするように構成できます。 次の構成は、デモアプリケーションで使用できます。
requirejs: { options: { paths: { 'appFiles': './app' }, removeCombined: true, out: './app/requirejs/appIdeas-combined.js', optimize: 'none', name: 'main' }, dev:{ options:{ optimize:'none' } }, release:{ options:{ optimize:'uglify' } } }
この設定は、Gruntがdevオプションで起動されたときに非圧縮ファイルを作成し、Gruntがreleaseオプションで起動された場合に縮小されたファイルを作成します。
おわりに
アプリケーションのサイズが特定のファイル数を超えると、依存関係の管理が難しくなります。 RequireJSのようなライブラリを使用すると、ファイルのアップロード順序を気にせずに依存関係を簡単に定義できます。 依存関係管理は、JavaScriptアプリケーションの不可欠な部分になりつつあります。 AngularJS 2.0には、AMDのサポートが組み込まれています。
更新:コメントで、どの依存関係マネージャーを使用しているか、最適なオプションだと思うものを聞くのは興味深いでしょう。