AngularのメタフレームワークAnalogを試す
はじめに
今回は、AnalogというAngularのメタフレームワークを試してみたので紹介します。
Analogとは
Analogは、Angularのフルスタックメタフレームワークです。
GDEで、NgrxのメンテナーもしているBrandon氏が主に開発しているOSSで、現在は次の機能が提供されています。
- Vite-powered
- AnalogはビルドツールとしてViteを使用しており、テストもVitestを使っているようです。
- File-based routing and API routes
- Next.jsやNuxt.jsの
pages
フォルダのようにディレクトリ構成でルーティングが決定する実装になっています。
- Next.jsやNuxt.jsの
- Hybrid SSR/SSG support
(upcoming features)SSRやSSGについてはViteの機能を上手く使おうとしているようで開発予定となっています。- 2023/4/3追記:AnalogがSSGとSSRをサポートしてました
- SSGの方はViteのprerenderを上手く使っているようです。
- SSRの方はAngularUniversalのrenderApplication関数を上手く使っているようです。
試してみる
アプリケーションを作成する
最初に、次のコマンドでプロジェクトを作成します。
npm create analog@latest
次に、プロジェクト名を聞かれるので、任意の名前に設定します。
Project name: analog-sample
その後、テンプレートを聞かれるので、「Analog」を選択します。
Select a template: › Analog
最後に、Angularのバージョンを聞かれるので、「angular-v15」を選択します。
Select a variant: › angular-v15
アプリケーションが作成されたら、package.json
を参照すると分かるとおり、普段のAngularアプリケーションのように、npmコマンドを実行できます。
npm run start
初期化時に生成されるtsconfig.jsonのtargetがES2022
になっており、ES2022のクラスの静的メソッド、static-class-featuresの辺りがSafariでエラーになっていたので、es2018
にします。tsconfig.jsonを設定する
"compilerOptions": {
...
- "target": "ES2022",
+ "target": "es2018",
},
...
また、自分が試した時は、vitestがtypescriptに上手く認識されていなかったので、"vitest"を追加して回避しました。
"compilerOptions": {
- "types": ["node", "vitest/globals"]
+ "types": ["node", "vitest/globals", "vitest"]
},
加えて、AnalogはStandalone Componentを前提としているため、angular-cliを使う場合は、angular.jsonに次の記述を追加するとng g c
で作成するコンポーネントがデフォルトでStandaloneとなり、作成しやすいです。
...
"test": {
"builder": "@nrwl/vite:test",
"options": {
"config": "vite.config.ts"
}
}
},
+ "schematics": {
+ "@schematics/angular:component": {
+ "standalone": true
+ }
+ }
...
ディレクトリ構成とルーティング
src/
├─ app/
│ ├─ routes/
│ │ ├─ index.ts
├─ assets/
├─ server/
│ ├─ routes/
│ │ ├─ v1/
│ │ │ ├─ hello.ts
アプリケーション初期化時には、このようなディレクトリ構成になっており、app/routes
がNext.jsやNuxt.jsのpages
に相当するものとなっており、フロントエンド側のルーティングが設定されます。Angularアプリケーションでよく使われる次のようなルーティング設定を記述しなくても動くようになっています。
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { TopComponent } from './pages/top/top.component';
const routes: Routes = [{ path: '', component: TopComponent }];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
})
export class AppRoutingModule {}
また、server/routes/v1
にサーバー側のAPIを記述することができます。
import { defineEventHandler } from 'h3';
export default defineEventHandler(() => ({ message: 'Hello World' }));
記述方法はViteを使っているため、Nuxt.jsと似ています。
そして、フロントエンド側からは、次のように呼び出すことができます。
import { inject, Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
@Injectable({
providedIn: 'root'
})
export class ApiService {
private http = inject(HttpClient);
getHello() {
return this.http.get<{ message: string }>('/api/v1/hello');
}
}
ルーティングの定義方法
Defining Routesにある通り、現状は、5種類のルーティングの定義方法がありました。
- Index Routes
-
src/app/routes/(home).ts
のように(home)とすることで、トップのルート/
で表示するコンポーネントを指定できます。
-
- Static Routes
-
src/app/routes/static.ts
とすることでファイル名そのままのルート/static
で表示するコンポーネントを指定できます。
-
- Dynamic Routes
-
src/app/routes/dynamic.[pageId].ts
とすることでファイル名そのままのルート/dynamic/:pageId
で表示するコンポーネントを指定できます。
-
- Nested Routes
- ディレクトリ構造を次のようにすることで
/products/以下を<router-outlet></router-outlet>する/products/配下のルートコンポーネントであるproducts.ts
、/productsで表示する(products-list).ts
、/products/:productIdで表示する[productId].ts
、のようにコンポーネントを指定できます。
- ディレクトリ構造を次のようにすることで
routes/
│ └── products/
│ ├──[productId].ts
│ └──(products-list).ts
└── products.ts
- Catch-all routes
-
src/app/routes/[...page-not-found].ts
とすることで、404ページのようなアプリケーション内でパスの指定がないルートに対して表示するコンポーネントを指定できます。
-
ルーティングに関してはサンプルアプリケーションを作成したので、興味があれば見てみてください。
komura-c-analog-sample.netlify.app/
github.com/komura-c/analog-sample
また、Analogのリポジトリにも実装サンプルがあります。https://github.com/analogjs/analog/tree/main/apps
おわりに
AngularのメタフレームワークAnalogを試してみました。現状試した感想は、Viteを使っているのでビルドが早いという所とルーティングの体験が簡単なのが良いなと感じました。今後が楽しみです。
また、開発者のBrandon氏は、Building Angular Applications with Viteという動画も公開しているので、見てみると理解が深まるかもしれません。
明日は、@masayasvielさんです。
Discussion