angular-best-practices について
モダンな Angular アプリを開発する人向けの実践ガイドです。TypeScript、シグナル、コンポーネント、性能、アクセシビリティ、ルーティング、テストを扱い、個別ライブラリ向けのスキルも示します。
主な機能
- 01Angular 開発に関する 112 のルール
- 02シグナルベースのコンポーネントとリアクティビティ
- 03テンプレートと描画の最適化
- 04SSR、ルーティング、バンドルの指針
- 05テストとアクセシビリティのパターン
活用シーン
- 01コンポーネント構成の見直し
- 02モダンな Angular シグナルパターンの適用
- 03SSR とルート読み込みの改善
- 04アクセシブルな Angular テストの計画
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ alfredoperez/ angular-best-practices --skill angular-best-practicesインストール後、Claude Code で/angular-best-practicesを入力して読み込まれたか確認します。
同梱ファイルAGENTS.mdevals/evals.json
詳細
モダンな Angular のベストプラクティス
TypeScript の厳格化、シグナルベースのリアクティビティ、コンポーネントアーキテクチャ、テンプレートの最適化、RxJS のパターン、SSR のハイドレーション、バンドルの最適化、アクセシビリティ、ルーティング、フォーム、テスト、スタイリングを網羅した、112 個のルールです。これらに従うことで、構築するすべてのコンポーネント、サービス、テンプレート、ルートを高速で、アクセスしやすく、テスト済みで、保守しやすいものにできます。
以下では、作業内容ごとに主要なパターンを整理しています。記載されていないエッジケースや具体的なコード例については、このスキルディレクトリにある AGENTS.md のリファレンスファイルを参照してください。
コンポーネントとシグナル
コンポーネントはアプリケーションを構成する基本要素です。モダンな Angular ではリアクティビティにシグナルを使用するため、状態からテンプレートまで、あらゆるコードの書き方が変わります。
ChangeDetectionStrategy.OnPushを設定したスタンドアロンコンポーネントを使用する- デコレーターの代わりに、シグナル関数の
input()、output()、model()を使用する - コンストラクターインジェクションの代わりに
inject()を使用する - ローカル状態には
signal()、派生状態にはcomputed()を使用する - ソースが変更されたときに状態をリセットする場合は
linkedSignal()を使用する - ロード状態が組み込まれた非同期データには
resource()/httpResource()を使用する effect()は副作用にのみ使用し、状態の同期には決して使用しない- RxJS オブザーバブルをシグナルベースのテンプレートに橋渡しするには
toSignal()を使用する - デコレーターの代わりに、シグナルクエリの
viewChild()/contentChild()を使用する @HostBinding/@HostListenerの代わりにhostプロパティを使用する
テンプレートとスタイル
テンプレートとスタイルは連携して機能します。アクセシビリティ、レイアウト、パフォーマンスはどちらにも関わります。
- 構造ディレクティブの代わりに、
@if、@for、@switchの制御フローを使用する - 画面下部にある重いコンテンツには
@deferを使用する @forループには必ずtrackを指定する- 画面上部に表示される画像には、
priorityを設定したNgOptimizedImageを使用する - テンプレート内のメソッド呼び出しの代わりに、純粋パイプを使用する
- 大きなリストには
CdkVirtualScrollViewportを使用する [ngClass]の代わりに[class.active]バインディングを使用する- テーマの値は CSS カスタムプロパティとして定義する
- 動きに関する設定を尊重するには
prefers-reduced-motionを使用する
サービスと RxJS
サービスはデータフロー、依存性注入、RxJS のパターンを扱います。HTTP、キャッシュ、オブザーバブルのライフサイクルは相互に関連する要素です。
takeUntilDestroyed()またはasyncパイプを使用して購読を解除する- 外側のストリームを維持するため、
catchErrorはswitchMapの内側に配置する - 最新の値だけを扱う場合は
switchMap、処理中は新しい値を無視する場合はexhaustMapを使用する - ストリームを共有するには
shareReplay({ bufferSize: 1, refCount: true })を使用する - 設定には
InjectionTokenとinject()を使用する - 横断的な関心事(認証、再試行、ロギング)には HTTP インターセプターを使用する
- API の境界で DTO をマッピングし、バックエンドのデータ構造をコンポーネントに持ち込まない
パフォーマンスと SSR
パフォーマンスに関するルールは、コンポーネント、テンプレート、インフラストラクチャにまたがります。SSR はルーティング、データ取得、ハイドレーション戦略に影響します。
- ルートリゾルバーで重要なデータを事前ロードし、ウォーターフォールを解消する
- ルートと重いビューを遅延ロードする
- モジュールではなくスタンドアロンコンポーネントのインポートを使って、インポートをツリーシェイクする
- レイアウトスラッシングを防ぐため、DOM の読み取りと書き込みをまとめて行う
- 頻繁な検索には通常のオブジェクトや配列より
Map/Setを使用する - 大きなページにはインクリメンタルハイドレーション(
@defer (hydrate on ...))を使用する - SSR には
provideClientHydration(withEventReplay())を使用する - ルートごとにレンダリングモードを設定する。SEO には SSR、ダッシュボードには CSR を使用する
テスト
テストのパターンはコンポーネント、サービス、テンプレートに共通して適用されます。分離は重要ですが、統合時のコンテキストも考慮する必要があります。
- DOM を直接クエリするより、コンポーネントハーネスを使用する
- 一貫性のあるテストデータを用意するため、テスト用オブジェクトファクトリーを作成する
- 実装ではなく、シグナルの状態変化とテンプレートの出力をテストする
- サービスのモックには
jasmine.createSpyObjまたはjest.fn()を使用する - アクセシビリティを
axe-coreまたはjest-axeでテストする
アーキテクチャとルーティング
アーキテクチャ上の決定は、ルーティング、モジュールの境界、依存性の注入など、すべてのファイル形式に影響する構造的なものです。
- 遅延読み込みするルートごとに機能を1つ割り当てます
- 認証にはガード、データにはリゾルバー、未保存の変更には
canDeactivateを使用します - 次に利用される可能性の高いルートには、プリロード戦略(
QuickLinkStrategy)を使用します withComponentInputBinding()とinput()を使用してルートパラメーターをバインドします- バレルファイルでの再エクスポートは避け、ソースから直接インポートします
- 環境に基づく設定を使用し、URLやAPIキーをハードコードしないでください
TypeScript の基礎
コンポーネント、サービス、テストを含む、すべての.tsファイルで適用します。
- tsconfigで
strict: trueを設定し、厳密な型チェックを有効にします anyを避け、型が不明な場合はunknownを使用し、ジェネリクスで型を絞り込みます- 型のみのインポートには
import typeを使用します - エクスポートする関数には明示的な戻り値の型を指定します
- 変更すべきでないデータには
readonlyを優先します - 状態のバリエーションには判別可能なユニオンを使用します
- 失敗する可能性がある操作にはResultパターンを使用します
アクセシビリティ
アクセシビリティはテンプレート、スタイル、コンポーネントに関わるものであり、HTMLだけの問題ではありません。
- まずセマンティックなHTML要素(
<nav>、<main>、<button>)を使用します - 動的なコンテンツにはARIAロールと
aria-live領域を使用します - すべてのインタラクティブ要素をキーボードで操作できるようにします
- ダイアログやオーバーレイには
cdkTrapFocusを使用します - スクリーンリーダーと
axe-coreでテストします
クイックリファレンス
| パターン | 使用するもの | 避けるもの |
|---|---|---|
| シグナル入力 | input<T>() | @Input() |
| シグナル出力 | output<T>() | @Output() |
| 双方向バインディング | model<T>() | 入力と出力の組み合わせ |
| 依存性の注入 | inject() | コンストラクターインジェクション |
| 制御フロー | @if、@for、@switch | *ngIf、*ngFor |
| クラスのバインディング | [class.active] | [ngClass] |
| 変更検知 | OnPush | Default |
| 派生状態 | computed() | ゲッター |
| ビュークエリー | viewChild() | @ViewChild() |
主要なコードパターン
シグナル入力と出力(デコレーターを置き換えます):
name = input<string>(); // @Input() replacement
save = output<Data>(); // @Output() replacement
value = model<string>(); // two-way binding
制御フロー(構造ディレクティブを置き換えます):
@if (user()) { <profile [user]="user()" /> }
@for (item of items(); track item.id) { <card [item]="item" /> }
@defer (on viewport) { <heavy-chart /> }
httpResourceとresource(シグナルベースの非同期処理):
users = httpResource<User[]>(() => `/api/users?role=${this.role()}`);
data = resource({ request: () => this.id(), loader: ({request}) => fetch(request) });
オプションのライブラリスキル
このコアスキルとともに、ライブラリ固有のルールをインストールします。
| ライブラリ | スキルページ |
|---|---|
| NgRx | angular-best-practices-ngrx |
| SignalStore | angular-best-practices-signalstore |
| TanStack Query | angular-best-practices-tanstack |
| Angular Material | angular-best-practices-material |
| PrimeNG | angular-best-practices-primeng |
| Spartan UI | angular-best-practices-spartan |
| Transloco | angular-best-practices-transloco |
リンク
- GitHubリポジトリ
- GitHub Issuesからルールを提案
- すべてのスキルを見る
ライセンス
MIT