Angular - フィーチャーモジュールの種類
Angular の フィーチャーモジュール
Angular を用いてそこそこな規模のアプリケーション制作を始めていくと、
すぐに AppModule がいっぱいいっぱいになってしまいます。
そこそこな規模の制作で Angular を用いる場合には、
アプリケーションを機能単位でモジュールというまとまりで管理するのが一般的です。
一般的な フィーチャーモジュールの作成
モジュールを利用したアプリケーション構築の例が、Angular の公式から提供されています。
コード例を確認しながら見ていきましょう。
ルーティング NgModule
ルーティング NgModule は、おそらく誰もが目にしたことのある NgModule でしょう。
ルートの設定に特化した NgModule で import と export のセクションで、
RouterModule を処理します。
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
export const routes: Routes = [
// ...
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule {}
ルーティング NgModule では、ルートで利用される Provider などが、
providers のセクションに登録される場面もあります。
ドメイン NgModule
ドメインは NgModule、最も一般的なフィーチャーモジュールです。
アプリケーションの特定の機能にあわせて、コンポーネントやサービス、ルーティングなどが格納されます。
ドメイン NgModule で ルーティングを定義する場合には、
RouterModule.forRoot
の代わりに、 RouterModule.forChild
を用います。
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
const routes = [
// ...
];
@NgModule({
imports: [ RouterModule.forChild(routes) ],
exports: [ RouterModule ]
})
export class ContactRoutingModule {}
モジュール本体のコードは、以下のような形で構成されます。
import のセクションには ReactiveFormsModule
など Module 内で利用するすべての Module を都度定義しなければならない点に注意してください。
import { NgModule } from '@angular/core';
@NgModule({
imports: [
// module 内で利用する Module
],
declarations: [
// module 内で利用するコンポーネント
],
providers: [
// module 内で完結したライフサイクルを持つ Service
]
})
export class ContactModule { }
このようにして定義した Module は 通常 AppModule に import して利用されます。
ルーテッド NgModule
アプリケーションが大規模になってきた場合、すべての ドメイン NgModule を
AppModule に登録するのは バンドルサイズの増大を招き 通信の観点から非効率が懸念されます。
機能単位で分割された NgModule を URL の第一セグメントに紐づけて遅延ロードさせることによって、
アプリケーションの初期バンドルサイズを削減することが期待できます。
ルーテッド NgModule は こうした遅延ロードされる Module を指すもので、
ルートの AppRoutingModule において以下のような形で import されます。
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
export const routes: Routes = [
{ path: '', redirectTo: 'contact', pathMatch: 'full'},
{ path: 'items', loadChildren: () => import('./items/items.module').then(m => m.ItemsModule) },
{ path: 'customers', loadChildren: () => import('./customers/customers.module').then(m => m.CustomersModule) }
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule {}
重要なことは ルーテッドモジュールを、例えば AppModule などで import してはいけないということです。
ルーテッドモジュールとして意図されたモジュールを、他の module で import した場合、
遅延ロードの効果は失われます。
ルーテッドモジュールの構成は ドメインモジュールと似ていますが、
ルーテッドモジュールは、該当の URL にアクセスするまで決してアクセスされることが無いため、
例えば モジュールの内部に injectedIn:"root"
のモジュールなどを含ませていた場合、
アプリケーションの中で発見しづらいバグをもたらす可能性があります。
Shared NgModule
アプリケーションの様々なモジュールから利用されるような機能を詰め込んだモジュールを
Shared NgModule と呼ぶケースがあります。
アプリケーションの中で利用する カスタムパイプやディレクティブなどをまとめたり、
特定の UI を実現するような コンポーネントをまとめたりするのに用いられます。
外部に公開するパイプやディレクティブ、コンポーネントなどの要素は、
一つづつ モジュールの exports セクションに登録する必要があります。
また、アプリケーション内に複数の ドメイン NgModule が存在する場合、
必要に応じてそれぞれの モジュール内で import する必要があるということも覚えておいてください。
参考
Discussion