AngularJsにおける遅延ロード
ここで2つの質問をします。
1. アプリケーションの起動後に、モジュールのファイル読み込みを延期するにはどうすればよいですか?
2. 選択したスクリプトローダーではなく、アプリケーションのどこで実際の読み込みを行うべきですか?
質問1は、アプリケーション起動後にモジュールAPIを使用してファイル登録ができないことが原因です。つまり、以下のように、起動後のアプリで新しいコントローラを作成したい場合です。
angular.module('app').controller('SomeLazyController', function($scope)
{
$scope.key = '...' ;
});
そして、このコントローラをng-controllerタグに関連付けると、以下のような例外が発生します。
Error: Argument 'SomeLazyController' is not a function, got undefined
現在、私が知る限り、起動後にアプリケーションにファイルを登録する方法は、モジュールAPIを使うのではなく、AngularJsのサードパーティサービスを使うしかありません。
サードパーティサービスは、AngularJsのファイルのインスタンスを作成したり、設定したりするためのオブジェクトで構成されています。そこで、コントローラの遅延登録のために $controllerProvider サービスを使用します。類似の例として、$compileProvider サービスはディレクティブの登録遅延に、$filterProvider サービスはフィルタの登録遅延に、$provider サービスはサービスの登録遅延に使用されます。以下は、コントローラとサービスの例です。
// Registering a controller after app bootstrap
$controllerProvider.register('SomeLazyController', function($scope)
{
$scope.key = '...' ;
});
// Registering a directive after app bootstrap
$compileProvider.directive('SomeLazyDirective', function()
{
return {
restrict: 'A',
templateUrl: 'templates/some-lazy-directive.html'
}
})
// etc
サードパーティーのサービスは、モジュールの設定中にのみ有効になります。そのため、互いに連絡を取り合い、ファイルの登録を遅らせるために利用されます。例えば、関連するサービスを保持しておくことで、以下の例のようにappモジュールを作成することができます。
appModule.js
(function()
{
var app = angular.module('app', []);
app.config(function($routeProvider, $controllerProvider, $compileProvider, $filterProvider, $provide)
{
app.controllerProvider = $controllerProvider;
app.compileProvider = $compileProvider;
app.routeProvider = $routeProvider;
app.filterProvider = $filterProvider;
app.provide = $provide;
// Register routes with the $routeProvider
});
})();
そして、以下のようにコントローラの登録を遅らせることができます。
someLazyController.js
angular.module('app').controllerProvider.resgister('SomeLazyController', function($scope)
{
$scope.key = '...' ;
});
しかし、これらのコントローラファイルの読み込みを、<script> の代わりにどこで遅延させるかという疑問が残ります。現在、これを行うことができるのは一箇所だけで、それはルートのプロパティが定義されている場所です。
ルート定義に $routeProvider サービスを使用する場合、キーとファクトリの依存関係 (ページと js ファイルの依存関係) のマップを指定して、ルートのコントローラにマッピングをインジェクトすることができます。このマップは 'resolve' という名前です。
$routeProvider.when('/about', {templateUrl:'views/about.html', controller:'AboutViewController' resolve:{key:factory});
マップの「キー」は依存関係の名前を表し、「ファクトリー」は依存関係として使われる既存のサービスの別名(文字列)か、その戻り値が依存関係として使われる注入可能なメソッド(関数)であることができます。メソッドがプロミスを返す場合,ルートがトリガーされる前にプロミスが実行されます.したがって、これらの依存関係は、遅延ファイルロードのように、非同期で再フェッチされることになります。依存関係マップのメソッドを使用して、ファイルが遅延された時点で実行されるプロミスを取得するのです。これにより、ルートがトリガーされる前にすべてのファイルが遅延ロードされることが保証されます。以下は、依存関係の遅延ロードのために $script.js ローダーの使用を指定したルート定義の例です。
$routeProvider.when('/about', {templateUrl:'views/about.html', resolve:{deps:function($q, $rootScope)
{
var deferred = $q.defer();
var dependencies =
[
'controllers/AboutViewController.js',
'directives/some-directive.js'
];
// Load the dependencies
$script(dependencies, function()
{
// all dependencies have now been loaded by so resolve the promise
$rootScope.$apply(function()
{
deferred.resolve();
});
});
return deferred.promise;
}}});
上記の例では、プロミスは$scriptjsのコンテキストで実行されますが、AngularJsのコンテキストでは実行されないことに注意してください。プロミスが実行されたらAngularJsに通知します。$rootScopeの$applyメソッドでプロミスを実行し、AngularJs通知の通知を有効にしてください。
$rootScope.$apply(function()
{
deferred.resolve();
});
rootScopeの$applyメソッドでプロミスが実行されないと、ページの初期化時にルートがトリガーされません。
さて、上記を応用してappモジュールを以下のように定義します。
appModule.js
(function()
{
var app = angular.module('app', []);
app.config(function($routeProvider, $controllerProvider, $compileProvider, $filterProvider, $provide)
{
app.controllerProvider = $controllerProvider;
app.compileProvider = $compileProvider;
app.routeProvider = $routeProvider;
app.filterProvider = $filterProvider;
app.provide = $provide;
// Register routes with the $routeProvider
$routeProvider.when('/', {templateUrl:'views/home.html'});
$routeProvider.when('/about', {templateUrl:'views/about.html', resolve:{deps:function($q, $rootScope)
{
var deferred = $q.defer();
var dependencies =
[
'controllers/AboutViewController.js',
'directives/some-directive.js'
];
$script(dependencies, function()
{
// all dependencies have now been loaded by $script.js so resolve the promise
$rootScope.$apply(function()
{
deferred.resolve();
});
});
return deferred.promise;
}}});
});
})();
最後に、$script.jsと同じ方法で、アプリを起動します。
appBootStrap.js
// This file will be loaded from index.html
$script(['appModule.js'], function()
{
angular.bootstrap(document, ['app'])
});
以上が、AngularJsで遅延ロードを実装するための大まかな手順です。簡単に説明すると、まずappモジュールが関連するプロバイダのインスタンスを保持していることを確認する必要があります。次に、ルート定義で 'resolve' メソッドを介してプロミスを返し、ルートがトリガーされたら、遅延されたファイルをロードし、プロミスを実行します。そして、アプリを起動する前に、アプリモジュールを初めて読み込むための「bootstrap」テキストを作成し、最後に「bootstrap」テキストを「index.html」に関連付けます。
元記事へのリンクです。 http://ify.io/lazy-loading-in-angularjs/
参考記事
関連
-
[解決済み】フォームコントロールの値アクセサがない
-
[解決済み] DIVコンテンツを更新する方法を教えてください。
-
[解決済み] ネイティブ・オブジェクトとホスト・オブジェクトの違いは何ですか?
-
[解決済み] MalformedXMLです。提供されたXMLは整形式でないか、公開されたスキーマに照らして検証されていません。
-
[解決済み] SyntaxError: JSON.parse: JSONの1行目2列目に予期せぬ文字があります。
-
[解決済み] Dropzone already attached "というエラーはどうすれば直るのですか?
-
[解決済み] Dojo: すべての xhr / ajax 呼び出しが同期化され、他の呼び出しをブロックしているように見える
-
[解決済み] React, Uncaught RangeError: 最大呼び出しスタックサイズを超えている
-
[解決済み] 文字列にプロパティを作成できない
-
[解決済み] Angularドラッグ&ドロップ式フォームビルダー【終了】のお知らせ
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] HTML5 <video> のクリックによる再生/一時停止/全画面表示を無効にする(YouTube用
-
[解決済み] Javascript + Regex = エラーを繰り返すことはない?
-
[解決済み] パスワードが一致しても、パスワードの確認で「パスワードが一致しません」と表示されることがある
-
[解決済み] PHPサイト(Bootstrap)のIMGBBにAJAX+Javascriptで画像アップロード
-
[解決済み] alert("Hi!") と function(){alert("Hi!")} の違い。重複] [重複] [重複] [重複] [重複] [重複
-
[解決済み] Phaser 3でのゲームの一時停止と再開
-
[解決済み] 新しいコンポーネントがAngularをレンダリングしない
-
[解決済み] document.querySelector(...) is null エラー
-
[解決済み] オブジェクトHTMLImageElementが画像の代わりに表示されます。
-
React ReferenceErrorの落とし穴を覚えよう。は定義されていません