Angular アプリケーションのリファクタリングは、諸刃の剣になる可能性があります。一方で、コードベースの保守性とスケーラビリティを向上させることができます。一方、意図しない変更から機能を保護するために必要な予防策を講じていない場合、ルートの破損につながる可能性があります。広範なテストを作成したり、ルートにしっかりとした型付けの概念を実装したりすると、このリスクを軽減できますが、これらのアプローチは時間がかかる可能性があり、常に実行可能であるとは限りません。この記事では、手動でのテスト作業やカスタム型の注釈の記述を必要とせずに、コンパイル時に壊れたルートを自動的に検出する、より効率的なソリューションを検討します。ネストされたコンポーネントを含むサンプル Angular アプリケーションを実装し、typesafe-routes ライブラリを使用して開発者のエクスペリエンスを向上させ、パラメーターの解析を容易にすることで、このアプローチを実証します。
コンパイル時に壊れたルートを自動的に検出する利点を説明するために、DashboardComponent (/dashboard)、OrgsComponent (/orgs/:orgId)、および LocationsComponent (/orgs) の 3 つのネストされたコンポーネントを含むサンプル Angular アプリケーションを実装します。 /:orgId/locations/:locationId)。この例を設定するには、次のコード部分に示すように、typesafe-routes ライブラリをインストールし、その createRoutes 関数を使用してルート ツリーを定義する必要があります。
// app.routes.ts import { createRoutes, int } from "typesafe-routes"; export const r = createRoutes({ dashboard: { path: ["dashboard"], // ~> "/dashboard" }, orgs: { path: ["orgs", int("orgId")], // ~> "/orgs/:orgId" children: { locations: { path: ["locations", int("locationId")], // ~> "locations/:locationId" query: [int.optional("page")], // ~> "?page=[number]" }, }, }, });
コードの断片を詳しく見てみましょう。 typesafe-routes から createRoutes をインポートし、最初の引数としてルートを渡します。これらのルートは、ルート レベルでダッシュボードと組織という 2 つのプロパティを持つネストされたオブジェクトとして定義されます。これらの各プロパティにはパスが割り当てられ、配列の形式でセグメントを指定します。たとえば、["dashboard"] 配列はパス /dashboard に対応します。 orgs パスには、整数型の orgId という名前のパラメータが含まれているため、より複雑です。 integer はネイティブ JavaScript 型ではなく、バックグラウンドで数値を使用して整数の特性を模倣する int 関数を使用して定義されたカスタム型であることに注意してください。 orgs ルートには Children プロパティがあり、locations という 1 つの子ルートを指定します。 location ルートは orgs ルートに似ていますが、int 型の追加のオプションの検索パラメータ ページを指定します。
createRoutes は、ルートに関する情報を使用して、Proxy オブジェクトでラップされたコンテキストを作成します。そのプロキシ オブジェクトの詳細を知る必要はありませんが、そのオブジェクトのおかげで、アプリケーション内のどこにでもすべてのルート仕様にアクセスして、ルートとパラメータをレンダリングおよび解析できることを理解することが重要です。
createRoutes によって返された Proxy オブジェクトを r に割り当てました。これは、r.dashboard を使用してダッシュボードのパス、r.orgs.locations を使用してロケーションのパスなどにアクセスできることを意味します。
ルートを定義したら、次のステップ、angular-router への登録に進むことができます。
// app.routes.ts import { createRoutes, int } from "typesafe-routes"; export const r = createRoutes({ dashboard: { path: ["dashboard"], // ~> "/dashboard" }, orgs: { path: ["orgs", int("orgId")], // ~> "/orgs/:orgId" children: { locations: { path: ["locations", int("locationId")], // ~> "locations/:locationId" query: [int.optional("page")], // ~> "?page=[number]" }, }, }, });
コードの一部は、以前に定義したルート ツリーをミラーリングする、Angular Router のネストされたルートを含む一般的なセットアップを示しています。ただし、一般的なプレーン文字列を使用してパス テンプレート (orgs/:orgId など) を指定する代わりに、typesafe-routes/angular-router からテンプレート関数をインポートし、それを使用してパス テンプレートを生成します。 DashboardComponent と OrgsComponent の場合は、対応するパス r.dashboard と r.orgs を指定して template を呼び出すだけで、テンプレートを取得できます。ただし、残りのコンポーネント LocationsComponent は OrgsComponent の子であるため相対パスが必要ですが、Angular Router の場合は絶対パス orgs/:orgId/locations/:locationId になるため、r.orgs.locations を使用して生成することはできません。ルート テンプレートをネストする場合は相対パスが必要です。
相対パスを生成するには、_ リンクを使用できます。これにより、アンダースコア文字より前のすべてが実質的に省略されます。この場合、template(r.orgs._.locations) を使用して相対パスを生成できます。これは、絶対パスをレンダリングする必要があるシナリオだけでなく、相対パスを必要とする状況でも同じルート ツリーを再利用できるため、便利な機能です。
この時点で、お気に入りの IDE (Visual Studio Code など) でオートコンプリートとタイプミス防止をすでに利用しています。また、createRoutes を使用すると、すべてのタイプを最初のルート定義まで追跡できるため、将来の変更によってルート パスのスペルミスやタイプミスが警告されます。
ルート テンプレートを指定したので、リンクのレンダリングに進みたいと思います。そのために、型のシリアル化や型チェックなどのリンクをレンダリングするレンダリング関数を利用する単純なコンポーネントを作成したいと考えています。次の例は、アプリケーション内の他のコンポーネントを参照するアンカー要素のリストをレンダリングするコンポーネントを示しています。
// app.routes.ts import { Routes } from "@angular/router"; import { template } from "typesafe-routes/angular-router"; export const routes: Routes = [ { path: template(r.dashboard), // ~> "dashboard" component: DashboardComponent, }, { path: template(r.orgs), // ~> "orgs/:orgId" component: OrgsComponent, children: [ { path: template(r.orgs._.locations), // ~> "locations/:locationId" component: LocationsComponent, }, ], }, ];
コード例では、typesafe-routes/angular-router から render と renderPath をインポートします。 renderPath はパスをレンダリングしますが、render はリンク リストのクエリ パラメータをさらにシリアル化します。また、r というプロキシ オブジェクトをインポートします。これにより、以前に定義されたルートに関する情報にアクセスし、レンダリングする目的のルートを定義できるようになります。
まず、renderPath 関数を使用して、dashboardLink と orgsLink を作成します。最初のパラメータとして、レンダリングされるルートのパスを表す前述のプロキシ オブジェクトを取ります。 2 番目のパラメーターは、app.routes.ts の createRoutes で以前に定義されたパラメーターの名前とタイプに一致するパラメーター値を持つレコードです。戻り値は、対応するコンポーネントに属するパスを含む文字列です。
3 番目の例の render 関数はパスと検索パラメータの両方をレンダリングするため、パラメータ定義にパスとクエリ プロパティが必要です。ここでの戻り値は、パスとクエリの 2 つのプロパティを持つオブジェクトです。 2 つのプロパティを [routerLink] 属性と [queryParams] 属性の値として設定します。
パラメータの解析は、typesafe-routes の重要な部分です。上記のルート定義中に、いくつかのパラメーターを定義し、それらに整数のような型 int を与えました。ただし、パラメーター値は Location オブジェクトなどのさまざまなソースから取得されるため、文字列ベースになります。便利なことに、typesafe-routes は、これらの文字列を解析して目的の型にキャストするヘルパー関数をエクスポートします。解析は、前に作成したプロキシ オブジェクト r に基づいて行われます。つまり、パラメータがどのルートに属しているかをライブラリに伝える必要があります。次の例では、2 つの一般的な解析シナリオを示してそれを示します。
// app.routes.ts import { createRoutes, int } from "typesafe-routes"; export const r = createRoutes({ dashboard: { path: ["dashboard"], // ~> "/dashboard" }, orgs: { path: ["orgs", int("orgId")], // ~> "/orgs/:orgId" children: { locations: { path: ["locations", int("locationId")], // ~> "locations/:locationId" query: [int.optional("page")], // ~> "?page=[number]" }, }, }, });
location.href orgs/1/location/2?page=5 の場合、Angular では this.route.snapshot.queryParams を使用して文字列ベースのクエリ パラメータにアクセスでき、文字列ベースのパス パラメータはこれを通じて提供されます。ルート.スナップショット.パラメータ。 r.orgs.locations および this.route.snapshot.queryParams で parseQuery を使用すると、ページ パラメーターを数値として持つオブジェクトを取得できます。 r.orgs._.locations および this.route.snapshot.params で parsePath を使用すると、解析された locationId を取得します。この場合、r.orgs._.locations は相対パスであり、_ リンクの前のすべてのセグメントが省略されるため、結果のオブジェクトには orgId が存在しません。
typesafe-routes の解析関数は多用途であり、parse を使用して location.href 文字列からすべてのパラメータを一度に直接抽出することもできます。
// app.routes.ts import { Routes } from "@angular/router"; import { template } from "typesafe-routes/angular-router"; export const routes: Routes = [ { path: template(r.dashboard), // ~> "dashboard" component: DashboardComponent, }, { path: template(r.orgs), // ~> "orgs/:orgId" component: OrgsComponent, children: [ { path: template(r.orgs._.locations), // ~> "locations/:locationId" component: LocationsComponent, }, ], }, ];
パラメータに関する型情報の抽出は、InferQueryParams、InferPathParams、または InferParams を介して可能です。ここでは、InferQueryParams ユーティリティ タイプのデモを示します。
// app.component.ts import { render, renderPath } from "typesafe-routes/angular-router"; import { r } from "./app.routes"; @Component({ selector: "app-root", imports: [RouterOutlet, RouterLink], template: ` <h1>Absolute Links</h1> <ul> <li><a [routerLink]="dashboardLink">Dashboard</a></li> <li><a [routerLink]="orgsLink">Org</a></li> <li> <a [routerLink]="locationLink.path" [queryParams]="locationLink.query"> Location </a> </li> </ul> <router-outlet></router-outlet> `, }) export class AppComponent { dashboardLink = renderPath(r.dashboard, {}); // ~> dashboard orgsLink = renderPath(r.orgs, { orgId: 123 }); // ~> orgs/123 locationLink = render(r.orgs.locations, { path: { orgId: 321, locationId: 654 }, query: { page: 42 }, }); // ~> { path: "orgs/321/location/654", query: { page: "42" }} } // ...
このチュートリアルを締めくくるために、ルートの信頼できる唯一の情報源である単一のルート ツリー r を作成しました。これに基づいて、コンポーネントを Angular Router に登録するために使用するテンプレートをレンダリングしました。動的パス セグメントとクエリ パラメーターを使用してパスをレンダリングしました。パラメータを解析して文字列値から対応する型に変換しました。私たちは、型定義を 1 つも書かずに、すべてをタイプセーフな方法で実行しました。新しい機能の開発中にバグを簡単に防止し、将来のリファクタリングをさらに容易にする、堅牢なルート ツリーを確立しました。
ただし、typesafe-routes には、さまざまな組み込みパラメータ タイプ、カスタム パラメータ タイプの簡単な統合、サブパスの操作、カスタム テンプレート文字列の定義など、さらに多くの機能があります。残念ながら、このチュートリアルですべてを説明することはできませんが、公式ドキュメントにアクセスして詳細を読むことができます。
もちろん、このチュートリアルで示した例に実装できる潜在的な改善点も数多くあります。たとえば、r.orgs.locations などのプロキシ オブジェクトに基づいたパス定義を受け取るリンク レンダリング用のカスタム ディレクティブです。もう 1 つの例は、Angular Router の Routes 配列を自動的に生成する関数です。これにより、重複したコードが効果的に排除され、最初のコード ブロックで createRoutes で作成されたルート ツリーとルートの同期を保つ必要がなくなりました。
ただし、これらは多くの貢献方法の中のほんの一部にすぎません。最も一般的な方法は、もちろん、共有したり、バグを報告したり、GitHub リポジトリで PR を開くことです。このライブラリを使用して、開発エクスペリエンスが向上すると思われる場合は、私にコーヒーをおごっていただいても構いません。フィードバックを残したり、質問したりできる Discord チャンネルもあります。
以上がAngular のタイプセーフ ルートでランタイム エラーを排除するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。