Angular
ionic
Ionic3

Ionic3でアプリを作る際のLazy Loadingへの対応

最近(2018年10月)、Ionic3でAndroidアプリを作って、Play Storeで公開しました。

そこに至るにはいろいろと罠がありましたので、その記録を思い出せるうちに書いて残しておこうと思います。

困ったり解決したりする度にツイートしてたからいけるはず……!!

いくつかの記事にわけて、シリーズとしてまとめておきます。

なお、なんとかリリースできたアプリはこちら。あなたの日本語入力速度を測定します。→フリックとローマジ 〜日本語入力スピード測定〜


シリーズ一覧

この記事では、Lazy Loadingについて扱いますが、PageとComponentの場合の話だけします。PipeやDirectiveの場合については触れませんが、基本的にComponentの場合と同じです。


Lazy Loading????

Lazy Loadingとは、アプリ内で使うモジュールを、必要な分だけ随時読み込んでいく仕組みのことです。

最初に全部いっぺんに読み込まないので、その分起動が早くなるメリットがあります。

詳しくはこちら。

Ionic and Lazy Loading Pt 1 | The Official Ionic Blog

IonicでLazy Loadingに対応する - Qiita


Lazy Loadingの時とそうでない時のモジュール宣言の違い

AngularやIonicで使用するモジュールは、どこかでモジュールとして宣言されていなければいけません。PageとかComponentとかPipeとかDirectiveとか、全部モジュールです。

具体的には、なんとか.module.tsというファイルの中で、@NgModuleデコレータを使って


hoge.module.ts

import { NgModule } from '@angular/core';

import { IonicPageModule } from 'ionic-angular';
import { HogePage } from './hoge';

@NgModule({
declarations: [//declaration(「宣言」という意味の英単語)
HogePage,//←宣言の中に「HogePage」を加える
],
imports: [
IonicPageModule.forChild(HogePage),
],
})
export class HogePageModule {}


という感じで宣言します。

この宣言を、hoge.module.tsで独自にやるか、もっと親玉のapp.module.tsでたくさんまとめて宣言するか、という違いが、Lazy Loadingかそうでないかの違いになります。

この宣言をごちゃ混ぜにして、一つのモジュールをapp.module.tsとそれぞれのmodule.tsの2箇所で宣言したりすると、エラーになります。

しかも、ionic cordova run android --deviceの時はエラーにならないのに、ionic cordova run --prod --releaseの段階になって初めてエラーになるので、気づきにくく、タチが悪いです。


エラーメッセージの例

Type Hoge in hoge.ts is part of the declarations of 2 modules: AppModule in src/app/app.module.ts and ComponentsModule in src/components/components.module.ts



Pageのモジュール宣言


Lazy Loading

それぞれのページについて、module.tsファイルがあり、そこで宣言する。


いっぺんに読み込む

app.module.tsで宣言


Componentのモジュール宣言


Lazy Loading

それぞれのコンポーネントについてmodule.tsで宣言するか、Component用の共有モジュールcomponents.module.tsでコンポーネントたちだけ一括で宣言する


いっぺんに読み込む

app.module.tsで宣言


以下に、Component用の共有モジュールの例をあげておきます。


components.module.ts

import { NgModule } from '@angular/core';

import { HogeComponent } from './hoge/hoge.component';
import { FugaComponent } from './fuga/fuga.component';
import { FooBarComponent } from './foo-bar/foo-bar.component';
import { CommonModule } from '@angular/common';//これについては後で解説します

@NgModule({
declarations: [HogeComponent,
FugaComponent,
FooBarComponent],
imports: [CommonModule],//これについては後で解説します
exports: [HogeComponent,
FugaComponent,
FooBarComponent]
})
export class ComponentsModule {}



Lazy Loadingの時とそうでない時の、ComponentやPageの呼び出し方の違い

通常の場合、ComponentやPageを呼び出す時には、該当するものをファイルの冒頭でimportして、コード内で使います。

が、Lazy Loadingの場合は、importしようにもまだモジュールが読み込まれていないため、importに失敗します。

なので、そもそもimport文も書かず、コード内で使用する時には、文字列で指定します。

文字列でいいの!?と思ってしまいますが、それでうまくやってくれます。すごいですね。


通常読み込みの例

import { HogePage } from '..hoge/hoge';

...
this.navCtrl.setRoot(HogePage);
...


LazyLoadingの例

//import文は不要

...
this.navCtrl.setRoot("HogePage");
...


IonicのLazy Loading

Ionic3では、ionic generateコマンドでpageやcomponentをgenerateすると、Lazy Loadingに対応した形でモジュール宣言が出力されます。

なので、Angularチュートリアルで勉強したようにapp.module.tsに改めて宣言を書き加えると、二重宣言でエラーになります。

結局何もしなくていいってことですね。読み込む時にimport文を書かず、呼び出しを文字列にすることだけ気をつければOK。

Componentについては、個別にmodule.tsが用意されるのではなく、コンポーネント用共有モジュールcomponents.module.tsが用意されます。


ngIf、ngForが動かない時

詳しくはこちらに書いてあります。

https://angular.io/guide/frequent-ngmodules#browsermodule-and-commonmodule

要約すると、Lazy Loadingで読み込むモジュールの中で*ngFor*ngIfを使いたい場合は、そのモジュールのmodule.tsCommonModuleをインポートせよ、ということです。

Componentを共用モジュールで宣言している場合は、その共用モジュールのmodule.tsでそのimportを行うことになります。上記のコード例を再掲しておきますね。


components.module.ts

import { NgModule } from '@angular/core';

import { HogeComponent } from './hoge/hoge.component';
import { FugaComponent } from './fuga/fuga.component';
import { FooBarComponent } from './foo-bar/foo-bar.component';
import { CommonModule } from '@angular/common';//ここと

@NgModule({
declarations: [HogeComponent,
FugaComponent,
FooBarComponent],
imports: [CommonModule],//ここです。
exports: [HogeComponent,
FugaComponent,
FooBarComponent]
})
export class ComponentsModule {}



おわりに

この記事は以上です。同時に、このシリーズも以上です。

次はiOS版を作っていくことになるかな〜。