AngularのStyle Guideを読む
はじめに
以下の公式のスタイルガイドを抜粋して紹介します。
スタイルガイドでは、プラクティスを次の3つに分類しています。
カテゴリ | 内容 |
■Do | 従うべき事柄 |
■Consider | 基本的に従うべき事柄。ガイドラインの背景を理解した上で、より良い方法があるなら、それを選択すべき。 |
■Avoid |
すべきでない事柄 |
全体的には、1つのファイルに1クラス、クラス名とファイル名を合わせる、クラスやファイル名には機能を含めるという考え方が一貫していると感じました。これらのスタイルと合わせて、公式チュートリアルのソースコードを読むと理解がより深まると思います。
なお、以下でSTYLEの項番はオリジナルとそれと合わせています。一部オリジナルの文書で欠番になっているものについては、この記事でも同様にスキップしています。
STYLE 01-01 ひとつの規則
■Do
1つのファイルには、コンポーネントやサービスなど1つのことを含める
■Consider
1つのファイルを400行以内に制限する
STYLE 01-02 小さな関数
■Do
小さい関数を定義する
■Consider
1つの関数を75行以内に制限する
STYLE 02-01 一般的な名付け方
■Do
すべてシンボルに対して一貫した名前を付ける
■Consider
機能に続いて種類を記載する。推奨例は、機能.種類.ts
STYLE 02-02 ドットとダッシュでファイル名を区切る
■Do
ダッシュで記述的な名前を区切る
■Do
機能名と種類はドットで区切る
■Do
機能名に続いて種類を示す名前を使う。推奨するパターンは、機能.種類.ts
■Do
次の標準的な種類名を使う。必要なら種類を追加してもよいが、多くなりすぎないように注意する
.service, .component, .pipe, .module, .directive
STYLE 02-03 シンボルとファイル名
■Do
それが表すことに関連する一貫した名前を使用する
■Do
クラス名にアッパーキャメルケースを使う
■Do
シンボル名とファイル名を合致させる
■Do
シンボルにその種類を表す以下の標準的なサフィックスを追加する
Component, Directive, Module, Pipe, Service
■Do
ファイル名にその種類を表す以下の標準的なサフィックスを追加する
.component.ts, .directive.ts, .module.ts, .pipe.ts, .service.ts
例
シンボル名 | ファイル名 |
@Component({ ... }) export class AppComponent { } |
app.component.ts |
@NgModule({ ... }) export class AppModule |
app.module.ts |
@Injectable() export class UserProfileService { } |
user-profile.service.ts |
STYLE 02-04 サービス名
■Do
すべてのサービスに対して、その機能に関連する一貫した名前を使う
■Do
サービスのクラス名にはサフィクスとしてServiceを使う。例えばDataServiceやHeroService
いくつかの用語は明確なサービスである。それらは典型的にerで終わる。メッセージを記録するサービス名にLoggerServiceより、Loggerを好むかもしれません。あなたのプロジェクトの中でその例外が受け入れられるか決めましょう。
例
シンボル名 | ファイル名 |
@Injectable() export class HeroDataService { } |
hero-data.service.ts |
@Injectable() export class Logger { } |
logger.service.ts |
STYLE 02-05 ブートストラップ
■Do
起動とプラットホームのためのロジックをmain.tsに格納する
■Do
起動ロジックにエラーハンドリングを含める
■Avoid
main.tsにアプリケーションのロジックを含めない。代わりにコンポーネントあるいはサービスにそれを配置する
STYLE 02-06 ディレクティブのセレクタ
■Do
ディレクティブのセレクタには、ローワーキャメルケースを使う
STYLE 02-07 コンポーネントのための慣習的なプリフィックス
■Do
ハイフンを用いたローワーキャメルケースのセレクタ。例えばadmin-users
■Do
コンポーネントのセレクタに慣習的なプリフィックスを使う。例えばプリフィックスtohはTour of Heroesを、adminは管理者機能を表す。
■Do
機能領域あるいはアプリケーションを識別するプリフィックスを使う
悪い例 | 良い例 |
selector: 'hero' | selector: 'toh-hero' |
selector: 'users' | selector: 'admin-users' |
STYLE 02-08 ディレクティブのための慣習的なプリフィックス
■Do
ディレクティブのセレクタに慣習的なプリフィックスを使う。例えば、Tour of Heroesからtoh
■Do
非要素セレクタをローワーキャメルケースで綴る。ただし、それがHTMLのネイティブな属性に合致しなければならない場合を除く
悪い例 | 良い例 |
selector: '[validate]' | selector: '[tohValidate]' |
STYLE 02-09 Pipeの名前
すべてのパイプに対して、その機能に関連する一貫した名前を使う
シンボル名 | ファイル名 |
@Pipe({ name: 'ellipsis' }) export class EllipsisPipe implements |
ellipsis.pipe.ts |
@Pipe({ name: 'initCaps' }) export class InitCapsPipe implements |
init-caps.pipe.ts |
STYLE 02-10 ユニットテストファイル名
■Do
テストスペックファイル名に、テスト対象と同じ名前を付ける
■Do
テストスペックファイル名にサフィックス.specを付ける
例
シンボル名 | ファイル名 |
Components | heroes.component.spec.ts hero-list.component.spec.ts hero-detail.component.spec.ts |
Services | logger.service.spec.ts hero.service.spec.ts filter-text.service.spec.ts |
STYLE 02-11 End-to-End (E2E) テストファイル名
■Do
end-to-endテストスペックファイルに、テストの特徴に続き、サフィックス.e2e-spec.を付ける
例
シンボル名 | ファイル名 |
End-to-End Tests | app.e2e-spec.ts heroes.e2e-spec.ts |
STYLE 02-12 Angular NgModule名
■Do
シンボル名にサフィックスModuleを付ける
■Do
ファイル名に拡張子.module.tsを付ける
■Do
機能とフォルダの後にモジュール名を付ける
■Do
ルーティングモジュールクラスにはサフィックスRoutingModuleを付ける
■Do
ルーティングモジュールのファイル名の末尾を-routing.module.tsにする
例
シンボル名 | ファイル名 |
@NgModule({ ... }) export class AppModule { } |
app.module.ts |
@NgModule({ ... }) export class HeroesRoutingModule { } |
app-routing.module.ts |
STYLE 03-01 クラス
■Do
クラスにアッパーキャメルケースを使う
悪い例 | 良い例 |
export class exceptionService | export class ExceptionService |
STYLE 03-02 定数
■Do
アプリケーションの実行中に値が変化しないなら、変数にconstを付ける
■Consider
const変数にローワーキャメルケースを使う
■Do
既存のUPPER_SNAKE_CASEスタイルのconst変数を許容する
例
export const mockHeroes = ['Sam', 'Jill']; // prefer |
export const VILLAINS_URL = 'api/villains'; // tolerate |
STYLE 03-03 インターフェイス
■Do
インターフェイス名にアッパーキャメルケースを使う
■Consider
インターフェイス名にIプリフィックスを使わない
■Consider
インターフェイスの変わりにクラスを使う
悪い例 | 良い例 |
import { IHero } from './hero.model.avoid'; |
import { Hero } from './hero.model'; |
STYLE 03-04 プロパティとメソッド
■Do
プロパティとメソッドにローワーキャメルケースを使う
■Avoid
プライベートなプロパティとメソッドにアンダスコアを付けない
悪い例 | 良い例 |
private _toastCount: number; | private toastCount: number; |
STYLE 03-06 import行の空白
■Consider
サードパーティとアプリケーション自身のimportの間に空行をひとつ入れる
■Consider
モジュールをアルファベット順に並べる
■Consider
複数のシンボルをアルファベット順に並べる
悪い例
import { ExceptionService, SpinnerService, ToastService } from '../../core';
import { Http } from '@angular/http';
import { Injectable } from '@angular/core';
import { Hero } from './hero.model';
良い例
import { Injectable } from '@angular/core';
import { Http } from '@angular/http';
import { Hero } from './hero.model';
import { ExceptionService, SpinnerService, ToastService } from '../../core';
STYLE 04-01 LIFT
■Do
コードを素早く配置(Locate)、ひと目で識別(Identify)でき、できるだけフラットな構造(Flat)で、重複しない(Dry)ようなアプリケーションの構造にする
STYLE 04-02 Locate
■Do
直感的で、シンプルで、素早いコードの格納を実現する
STYLE 04-03 Identify
■Do
すぐにそれが含むものと表すものが分かるようにファイル名を付ける
■Do
ファイル名を使って説明可能にし、厳密にひとつのコンポーネントに内容を収める
■Avoid
1つのファイルに複数のコンポーネント、サービス、あるいは、ミクスチャを含めない
STYLE 04-04 Flat
■Do
できるだけフラットなフォルダ構造を保つ
■Consider
フォルダに7つかそれ以上のファイルがある場合、サブフォルダを作成する
■Consider
無関係で注意をそらすファイル (例えば .js, .js.map)を隠すようにIDEを設定する
STYLE 04-05 DRY
■Do
重複しない
■Avoid
可読性を犠牲にするほど簡潔にしすぎない
STYLE 04-06 全体的な構造のガイドライン
■Do
小さく始めるが、アプリが進むべき方向を心に留める
■Do
短い期間と長い期間の実装の見通しを持つ
■Do
アプリケーションのすべてのコードをsrc以下に配置する
■Consider
コンポーネントが複数の関連するファイル(.ts, .html, .css, .spec)を持つ場合、そのためのフォルダを作る
STYLE 04-07 機能ごとのフォルダ構造
■Do
機能領域を示す名前をもつフォルダを作る
■Do
各機能領域に対してAngularモジュールを作る
STYLE 04-08 アプリケーションのルートモジュール
■Do
アプリケーションのルートフォルダ (例えば /src/app)にAngularモジュールを作る
■Consider
ルートモジュールをapp.module.tsと名付ける
STYLE 04-09 機能モジュール
■Do
アプリケーションですべての異なる機能に対してAngularモジュールを作る
■Do
機能モジュールを同じ機能領域名をもつフォルダに配置する。例えば、app/heroes
■Do
機能領域とフォルダ名を反映した名前を機能モジュールに付ける。例えば、app/heroes/heroes.module.ts
■Do
機能領域、フォルダ、ファイルの名前を反映した名前を機能モジュールに付ける。例えば、app/heroes/heroes.module.tsはHeroesModuleを定義する。
STYLE 04-10 共有される機能モジュール
■Do
sharedフォルダにSharedModuleと名付けられた機能モジュールを作る。例えば、app/shared/shared.module.tsは、SharedModuleを定義する
■Do
コンポーネント、ディレクティブ、パイプが再利用され、他の機能モジュールで宣言されるコンポーネントによって参照される場合、それらを共有モジュールの中に作る
■Consider
共有モジュールの内容が、アプリケーション全体に渡って参照される場合、SharedModuleと名付ける
■Do
共有モジュールの中でサービスを提供しない。サービスは大抵シングルトンであり、アプリケーションまたは特定の機能モジュールで一度だけ提供される
■Do
SharedModuleの中のアセットにより要求されるすべてのモジュールをインポートする。例えば、CommonModuleやFormsModule
■Do
SharedModuleの中ですべてのコンポーネント、ディレクティブ、パイプを宣言する
■Do
SharedModuleの中で他のモジュールに必要なすべてのシンボルをエクスポートする
■Avoid
アプリケーションワイドなシングルトンプロバイダーをSharedModuleの中に記述しない
意図したシングルトンはOK。注意してください。
STYLE 04-11 コア機能モジュール
■Consider
機能モジュールの見た目の構造を簡潔にするため、コアモジュール内に多数の補助的な使い捨てクラスを集める
■Consider
アプリケーションワイドなコアモジュールをCoreModuleと呼ぶ。ルートのAppModuleでCoreModuleをインポートすることは、複雑さを低減し、アプリケーション全体のオーケストレータとしての役割を強調する。
■Do
coreフォルダ内にCoreModuleと名付けられた機能モジュールを作る。例えば、app/core/core.module.tsはCoreModuleを定義する。
■Do
CoreModuleでアプリケーションを通じて共有されるシングルトンのサービスを配置する。例えば、ExceptionServiceとLoggerService
■Do
CoreModuleの中でアセットに必要とされるすべてのモジュールをインポートする。例えば、CommonModuleとFormsModule
■Do
CoreModuleに、アプリケーションワイドの使い捨てコンポーネントを集める。アプリケーション開始後、それがどこからもインポートされないなら、1度だけ (AppModuleで) それをインポートする
■Avoid
AppModule以外でCoreModuleをインポートしない
■Do
AppModuleは、他の機能モジュールが使うためにインポートするCoreModuleのすべてのシンボルをエクスポートする
STYLE 04-12 コアモジュールの再インポートの抑止
■Do
CoreModuleの再インポートを抑止し、ガードロジックですぐに失敗させる
STYLE 04-13 レイジーロードフォルダ
異なるアプリケーションの機能、または、ワークフローは、アプリケーションの開始時ではなく、遅延してロードされるか、オンデマンドでロードすることができる
■Do
遅延して読み込みれる内容は、レイジーロードフォルダに配置する。典型的なレイジーロードフォルダは、ルーティングコンポーネント、その子コンポーネント、それらに関連するアセットとモジュールを含む
STYLE 04-14 直接インポートされないレイジーロードフォルダ
■Avoid
遅延してロードされる機能モジュールを直接インポートする兄弟および親フォルダの中のモジュールを許容しない
STYLE 05-02 コンポーネントのセレクタ名
■Do
ダッシュケースまたはケバブケースをコンポーネントの要素セレクタの名前に使う
悪い例 | 良い例 |
selector: 'tohHeroButton', | selector: 'toh-hero-button', |
STYLE 05-03 要素としてのコンポーネント
■Do
属性やクラスセレクタとは対象的に、要素セレクタをコンポーネントに与える
STYLE 05-04 テンプレートとスタイルファイルの展開
■Do
3行より大きいテンプレートとスタイルを独立したファイルに展開する
■Do
テンプレートファイルを[component-name].component.htmlと名付ける。ここで[component-name]はコンポーネントの名前である。
■Do
スタイルファイルを[component-name].component.cssと名付ける。[component-name]はコンポーネントの名前である。
■Do
コンポーネントの相対的なURLをプリフィックス./付きで指定する
STYLE 05-12 inputおよびoutputプロパティ修飾
■Do
@Input()および@Output()クラス修飾子を@Directiveおよび@Componentのメタデータのinputおよびoutputプロパティの代わりに使う
■Do
@Input()もしくは@Output()を、それを修飾するプロパティと同じ行に配置する
STYLE 05-13 inputおよびoutputのエイリアス抑止
■Avoid
inputおよびouputエイリアスをインポート目的で提供する場合を除き使用しない
STYLE 05-14 メンバの順序
■Do
プロパティを上部に、続いてメソッドを配置する
■Do
プライベートメンバをパブリックメンバの後にアルファベット順で配置する
STYLE 05-15 複雑なコンポーネントロジックをサービスに移譲する
■Do
コンポーネントの中ではビューに必要なのロジックのみに制限する。他のすべてのロジックをサービスに移譲する
■Do
再利用できるロジックをサービスに移して、コンポーネントをシンプルに維持し、それらの目的に集中する
STYLE 05-16 outputプロパティをプリフィックスにしない
■Do
プリフィックスonを用いずにeventに名前をつける
■Do
イベントハンドラメソッドの名前にプリフィックスonとそれに続いてイベント名である名前を付ける
STYLE 05-17 コンポーネントクラスの中にプレゼンテーションロジックを配置する
■Do
コンポーネントクラスの中のプレゼンテーションロジックを、テンプレートではなく、コンポーネントクラスに配置する。
STYLE 06-01 エレメントを強化するためにディレクティブを使う
■Do
テンプレートなしでプレゼンテーションロジックを持つ場合、属性ディレクティブを使う
STYLE 06-03 HostListener/HostBinding修飾子とhostメタデータ
■Consider
@Directiveおよび@Component修飾子のhostプロパティよりも、@HostListenerと@HostBindingを選ぶ
■Do
いずれの場合でもあなたの選択に一貫性を持つ
STYLE 07-01 サービスはシングルトンである
■Do
同じInjectorをもつシングルトンとしてサービスを使う
STYLE 07-02 単一の責任
■Do
そのコンテキストに結び付けられる単一の責務をもつサービスを作る
■Do
1つの目的を超えた時点で、新しいサービスを作る
STYLE 07-03 サービスの提供
■Do
サービスが共有される場合、その最上位レベルのコンポーネントのAngular Injectorにサービスを供給する
STYLE 07-04 @Injectable()クラス修飾子を使う
■Do
サービスの依存性識別子として型を用いる場合、@Injectパラメータの代わりに@Injectable()クラス修飾子を使う
STYLE 08-01 サービスを介してサーバと通信する
■Do
データ処理、および、サービスとデータをやりとするロジックをリファクタリングする
■Do
データサービスにXHRコール、ローカルストレージ、メモリへの格納、あるいは他のデータ操作の任せる
STYLE 09-01 ライフサイクルフックインターフェイスを実装する
■Do
ライフサイクルフックインターフェイスを実装する