SPA 3強を比較

初版作成
2024-09-12
目次

事前準備

$ node -v
v22.8.0

$ npm -v
10.8.2

Angular

# Angular 用のコマンドをインストールする
$ npm install -g @angular/cli
$ ng version
Angular CLI: 18.2.4

# プロジェクトを作る
$ ng new practice-angular --standalone=true --style=css --routing=false --ssr=false --minimal=true --inline-style=false --inline-template=false
$ tree ./practice-angular/ -I '.git|node_modules'
./practice-angular/
├── README.md
├── angular.json
├── package.json
├── public
│   └── favicon.ico
├── src
│   ├── app
│   │   ├── app.component.css
│   │   ├── app.component.html
│   │   ├── app.component.ts
│   │   └── app.config.ts
│   ├── index.html
│   ├── main.ts
│   └── styles.css
├── tsconfig.app.json
└── tsconfig.json

# 開発サーバをとりあえず起動する
$ cd ./practice-angular/
$ npm start -- --host 0.0.0.0
# サービスクラスを作るコマンドはこんな感じ
$ npm run ng generate service app
CREATE src/app/app.service.ts (132 bytes)
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { AppService } from './app.service';

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [CommonModule],  // 必要なモジュールを読み込む
  templateUrl: './app.component.html',
  styleUrl: './app.component.css'
})
export class AppComponent {
  // 画面に表示されるメッセージ
  public message: Promise<string> | null = null;
  
  // サービスクラスを使う宣言
  constructor(private readonly appService: AppService) { }
  
  // 画面初期表示時に、サービスクラスからデータを取ってくる
  public ngOnInit(): void {
    this.message = this.appService.generateMessage();
  }
}
<h1>Hello : {{ message | async }}</h1>
import { Injectable } from '@angular/core';

@Injectable({ providedIn: 'root' })
export class AppService {
  // 3秒待ってから「Test Text」という文字列を返す関数
  public async generateMessage(): Promise<string> {
    await new Promise((resolve) => setTimeout(resolve, 3000));
    return 'Test Text';
  }
}

→ コレで、画面を開くと3秒後に「Test Text」と表示されるサンプルページが作れた。


Vue

# プロジェクトを作る
$ npm create vue@latest
✔ Project name: … practice-vue
✔ Add TypeScript? … No / [Yes]
✔ Add JSX Support? … [No] / Yes
✔ Add Vue Router for Single Page Application development? … [No] / Yes
✔ Add Pinia for state management? … [No] / Yes
✔ Add Vitest for Unit Testing? … [No] / Yes
✔ Add an End-to-End Testing Solution? › No
✔ Add ESLint for code quality? … [No] / Yes
✔ Add Vue DevTools 7 extension for debugging? (experimental)[No] / Yes

$ tree ./practice-vue/
./practice-vue/
├── README.md
├── env.d.ts
├── index.html
├── package.json
├── public
│   └── favicon.ico
├── src
│   ├── App.vue
│   ├── assets
│   │   ├── base.css
│   │   ├── logo.svg
│   │   └── main.css
│   ├── components
│   │   ├── HelloWorld.vue
│   │   ├── TheWelcome.vue
│   │   ├── WelcomeItem.vue
│   │   └── icons
│   │       ├── IconCommunity.vue
│   │       ├── IconDocumentation.vue
│   │       ├── IconEcosystem.vue
│   │       ├── IconSupport.vue
│   │       └── IconTooling.vue
│   └── main.ts
├── tsconfig.app.json
├── tsconfig.json
├── tsconfig.node.json
└── vite.config.ts

# とりあえず開発サーバを起動する
$ cd ./practice-vue/
$ npm install
$ npm run dev -- --host=0.0.0.0

「Vue Official vue.volar」という VSCode 拡張機能を入れることで、.vue ファイルにシンタックスハイライト (色) が付くようになる。

不要なサンプルファイルを消して、Angular 版と似たようなコードを書いてみる。

<script setup lang="ts">
import HelloWorld from './components/HelloWorld.vue';
</script>

<template>
  <h1>Vue App</h1>
  <HelloWorld />
</template>

<style scoped>
h1 {
  color: #f00;
}
</style>
<script setup lang="ts">
import { onMounted, ref } from 'vue';
import { HelloService } from '../services/HelloService';

const message = ref('No Value');

// 画面初期表示時に、サービスクラスからデータを取ってくる
onMounted(async () => {
  message.value = await HelloService.generateMessage();
});
</script>

<template>
  <h2>{{ message }}</h2>
</template>

<style scoped>
h2 {
  color: #0c0;
}
</style>
class HelloServiceSingleton {
  // インスタンスを控えるプロパティ
  private static instance: HelloServiceSingleton;
  // 外部からのインスタンス生成を防ぐためプライベート・コンストラクタを宣言しておく
  private constructor() { }
  // シングルトンなインスタンスを生成する
  public static getInstance(): HelloServiceSingleton {
    if(HelloServiceSingleton.instance == null) HelloServiceSingleton.instance = new HelloServiceSingleton();
    return HelloServiceSingleton.instance;
  }
  
  // 3秒待ってから「Test Text」という文字列を返す関数
  public async generateMessage(): Promise<string> {
    await new Promise((resolve) => setTimeout(resolve, 3000));
    return 'Test Text';
  }
}

export const HelloService = HelloServiceSingleton.getInstance();

→ コレで、画面を開くと3秒後に「Test Text」と表示されるサンプルページが作れた。

オマケ : Vite の vue-ts で作った場合

Vite が用意する雛形の方が、Vue 公式の雛形よりは若干簡素だが、ほぼ差はない。

$ npm create vite@latest practice-vue-vite -- --template vue-ts

$ tree ./practice-vue-vite/
./practice-vue-vite/
├── README.md
├── index.html
├── package.json
├── public
│   └── vite.svg
├── src
│   ├── App.vue
│   ├── assets
│   │   └── vue.svg
│   ├── components
│   │   └── HelloWorld.vue
│   ├── main.ts
│   ├── style.css
│   └── vite-env.d.ts
├── tsconfig.app.json
├── tsconfig.json
├── tsconfig.node.json
└── vite.config.ts

React

# プロジェクトを作る
$ npm create vite@latest practice-react -- --template react-ts

$ tree ./practice-react/
./practice-react/
├── README.md
├── eslint.config.js
├── index.html
├── package.json
├── public
│   └── vite.svg
├── src
│   ├── App.css
│   ├── App.tsx
│   ├── assets
│   │   └── react.svg
│   ├── index.css
│   ├── main.tsx
│   └── vite-env.d.ts
├── tsconfig.app.json
├── tsconfig.json
├── tsconfig.node.json
└── vite.config.ts

# とりあえず開発サーバを起動する
$ cd ./practice-react/
$ npm install
$ npm run dev

「React zhang-renyang.vscode-react」という VSCode 拡張機能を入れることで、.tsx (.jsx) ファイルにシンタックスハイライト (色) が付くようになる。

不要なサンプルファイルを消して、Angular 版、Vue 版と似たようなコードを書いてみる。

import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import App from './App.tsx';
import './index.css';

createRoot(document.getElementById('root')!).render(
  <StrictMode>
    <h1>Hello</h1>
    <App />
  </StrictMode>
);
h1 { color: #f00; }
import { useEffect, useState } from 'react';
import { HelloService } from './HelloService';
import './App.css';

function App() {
  const [message, setMessage] = useState('No Value');
  
  useEffect(() => {  // 特定の処理を1回だけ行うための `useEffect`
    (async () => {
      // 画面初期表示に、サービスクラスからデータを取ってくる
      const generatedMessage = await HelloService.generateMessage();
      setMessage(generatedMessage);
    })();
  }, []);
  
  return (
    <>
      <h2>{ message }</h2>
    </>
  )
}

export default App;

→ コレで、画面を開くと3秒後に「Test Text」と表示されるサンプルページが作れた。