シングルページ アプリが大きくなるにつれて、ダウンロード時間も長くなります。これはユーザー エクスペリエンスにとってあまり良いことではありません (ヒント: しかし、シングル ページ アプリを開発するのはユーザー エクスペリエンスのためです)。コードが増えるとファイルが大きくなります。コード圧縮がニーズを満たさなくなるまで、ユーザーにできる唯一のことは、アプリケーション全体を一度にダウンロードするように要求するのをやめる事です。ここで遅延読み込みが役に立ちます。すべてのファイルを一度にダウンロードするのではなく、ユーザーは現在必要なファイルのみをダウンロードできます。
それで。アプリケーションを遅延読み込みするにはどうすればよいですか?それは基本的に2つのことに分かれます。モジュールを小さなチャンクに分割し、これらのチャンクをオンデマンドでロードできるようにするメカニズムを実装します。とても大変な仕事だと思いませんか? Webpack を使用している場合はそうではありません。すぐに使えるコード分割機能をサポートします。この記事では、読者が Webpack に精通していることを前提としていますが、そうでない場合は、ここで概要を説明します。簡単に説明すると、AngularUI Router と ocLazyLoad も使用します。
コードは GitHub で入手できます。いつでもフォークできます。
Webpack 構成
特に何もありません。実際には、ドキュメントからコピーして貼り付けるだけで済みます。唯一の違いは、コードをクリーンに保つために ng-annotate を使用することと、ECMAScript 2015 のマジックの一部を使用するために babel を使用することです。 ES6 に興味がある場合は、この前の投稿をチェックしてください。これらはすべて優れていますが、遅延読み込みを実装するためにはどれも必要ありません。
// webpack.config.js var config = { entry: { app: ['./src/core/bootstrap.js'], }, output: { path: __dirname + '/build/', filename: 'bundle.js', }, resolve: { root: __dirname + '/src/', }, module: { noParse: [], loaders: [ { test: /\.js$/, exclude: /node_modules/, loader: 'ng-annotate!babel' }, { test: /\.html$/, loader: 'raw' }, ] } }; module.exports = config;
適用
アプリケーション モジュールはメイン ファイルであり、bundle.js に含める必要があり、すべてのページでダウンロードする必要があります。ご覧のとおり、グローバルな依存関係を除いて、複雑なものは何も読み込んでいません。コントローラーのロードとは異なり、ルーティング構成のみをロードします。
// app.js 'use strict'; export default require('angular') .module('lazyApp', [ require('angular-ui-router'), require('oclazyload'), require('./pages/home/home.routing').name, require('./pages/messages/messages.routing').name, ]);
ルーティング構成
すべての遅延読み込みはルート設定で実装されます。先ほども述べたように、入れ子になったビューを実装する必要があるため、AngularUI Router を使用しています。いくつかの使用例があります。モジュール全体 (子ステート コントローラーを含む) をロードすることも、ステートごとに 1 つのコントローラー (親ステートへの依存関係に関係なく) をロードすることもできます。
モジュール全体をロードします
ユーザーが /home パスを入力すると、ブラウザはホーム モジュールをダウンロードします。これには、home と home.about の 2 つの状態に対応する 2 つのコントローラーが含まれています。状態設定オブジェクトのsolve属性を通じて遅延読み込みを実装できます。 Webpack の require.ensure メソッドのおかげで、最初のコード ブロックとしてホーム モジュールを作成できます。これは 1.bundle.js と呼ばれます。 $ocLazyLoad.load がないと、Angular の設計ではアプリケーションの起動後にファイルをロードすることが不可能なため、 Argument 'HomeController' is not a function, got unfineed というエラーが発生することがわかります。 ただし、$ocLazyLoad.load を使用すると、起動フェーズ中にモジュールを登録し、ロード後にそれを使用できます。
// home.routing.js 'use strict'; function homeRouting($urlRouterProvider, $stateProvider) { $urlRouterProvider.otherwise('/home'); $stateProvider .state('home', { url: '/home', template: require('./views/home.html'), controller: 'HomeController as vm', resolve: { loadHomeController: ($q, $ocLazyLoad) => { return $q((resolve) => { require.ensure([], () => { // load whole module let module = require('./home'); $ocLazyLoad.load({name: 'home'}); resolve(module.controller); }); }); } } }).state('home.about', { url: '/about', template: require('./views/home.about.html'), controller: 'HomeAboutController as vm', }); } export default angular .module('home.routing', []) .config(homeRouting);
コントローラーはモジュールの依存関係として扱われます。
// home.js 'use strict'; export default angular .module('home', [ require('./controllers/home.controller').name, require('./controllers/home.about.controller').name ]);
コントローラーのみをロード
私たちがやっていることは前進の第一歩であり、それから次のステップに進みましょう。今回は大きなモジュールはなく、合理化されたコントローラーのみになります。
// messages.routing.js 'use strict'; function messagesRouting($stateProvider) { $stateProvider .state('messages', { url: '/messages', template: require('./views/messages.html'), controller: 'MessagesController as vm', resolve: { loadMessagesController: ($q, $ocLazyLoad) => { return $q((resolve) => { require.ensure([], () => { // load only controller module let module = require('./controllers/messages.controller'); $ocLazyLoad.load({name: module.name}); resolve(module.controller); }) }); } } }).state('messages.all', { url: '/all', template: require('./views/messages.all.html'), controller: 'MessagesAllController as vm', resolve: { loadMessagesAllController: ($q, $ocLazyLoad) => { return $q((resolve) => { require.ensure([], () => { // load only controller module let module = require('./controllers/messages.all.controller'); $ocLazyLoad.load({name: module.name}); resolve(module.controller); }) }); } } })
ここには特別なことは何もなく、ルールは同じままでいいと思います。
ビューの読み込み
それでは、コントローラーから少しの間手を放して、ビューに集中してみましょう。お気づきかと思いますが、ルーティング設定にビューを埋め込んでいます。すべてのルーティング設定を Bundle.js 内に入れなくても問題はありませんが、ここではそうする必要があります。このケースは遅延読み込みルート設定ではなくビューに関するものなので、Webpack を使用して実装すると非常に簡単になります。
// messages.routing.js ... .state('messages.new', { url: '/new', templateProvider: ($q) => { return $q((resolve) => { // lazy load the view require.ensure([], () => resolve(require('./views/messages.new.html'))); }); }, controller: 'MessagesNewController as vm', resolve: { loadMessagesNewController: ($q, $ocLazyLoad) => { return $q((resolve) => { require.ensure([], () => { // load only controller module let module = require('./controllers/messages.new.controller'); $ocLazyLoad.load({name: module.name}); resolve(module.controller); }) }); } } }); } export default angular .module('messages.routing', []) .config(messagesRouting);
依存関係の重複に注意してください
messages.all.controller とmessages.new.controller の内容を見てみましょう。
// messages.all.controller.js 'use strict'; class MessagesAllController { constructor(msgStore) { this.msgs = msgStore.all(); } } export default angular .module('messages.all.controller', [ require('commons/msg-store').name, ]) .controller('MessagesAllController', MessagesAllController); // messages.all.controller.js 'use strict'; class MessagesNewController { constructor(msgStore) { this.text = ''; this._msgStore = msgStore; } create() { this._msgStore.add(this.text); this.text = ''; } } export default angular .module('messages.new.controller', [ require('commons/msg-store').name, ]) .controller('MessagesNewController', MessagesNewController);
問題の原因は require('commons/msg-store').name です。コントローラー間でのメッセージ共有を実現するには、msgStore サービスが必要です。このサービスは両方のパッケージに存在します。 1 つはmessages.all.controller に、もう 1 つはmessages.new.controllerにあります。もはや、最適化の余地はありません。どうやって解決すればいいでしょうか?アプリケーション モジュールの依存関係として msgStore を追加するだけです。これは完璧ではありませんが、ほとんどの場合、これで十分です。
// app.js 'use strict'; export default require('angular') .module('lazyApp', [ require('angular-ui-router'), require('oclazyload'), // msgStore as global dependency require('commons/msg-store').name, require('./pages/home/home.routing').name, require('./pages/messages/messages.routing').name, ]);
单元测试的技巧
把 msgStore 改成是全局依赖并不意味着你应该从控制器中删除它。如果你这样做了,在你编写测试的时候,如果没有模拟这一个依赖,那么它就无法正常工作了。因为在单元测试中,你只会加载这一个控制器而非整个应用模块。
// messages.all.controller.spec.js 'use strict'; describe('MessagesAllController', () => { var controller, msgStoreMock; beforeEach(angular.mock.module(require('./messages.all.controller').name)); beforeEach(inject(($controller) => { msgStoreMock = require('commons/msg-store/msg-store.service.mock'); spyOn(msgStoreMock, 'all').and.returnValue(['foo', ]); controller = $controller('MessagesAllController', { msgStore: msgStoreMock }); })); it('saves msgStore.all() in msgs', () => { expect(msgStoreMock.all).toHaveBeenCalled(); expect(controller.msgs).toEqual(['foo', ]); }); });
以上内容是小编给大家分享的Webpack 实现 AngularJS 的延迟加载,希望对大家有所帮助!