🍣
主要なSPAフレームワークまとめ
事前知識
SPAとは
Single Page Applicationの略
1つのWebページで動作するアプリケーションのこと
モダンなJSフレームワークを使って開発されることが多い
本記事では、React、Vue、Angularに絞って比較
主要なSPAフレームワーク
表にまとめると、下記の通り
フレームワーク | 特徴 | 学習曲線 | 市場シェア | 状態管理 | データフロー |
---|---|---|---|---|---|
React | コンポーネントベース、豊富なエコシステム | 中程度 | 40%くらい | Hooks、またはライブラリ | 単方向のみ |
Vue.js | 直感的な構文、段階的に導入可能 | 低め | 25%くらい | Composition API + Reactive、またはVuexなどのライブラリ | 単方向/双方向 |
Angular | フルスタック機能、TS標準 | 高め | 15%くらい | Zone.js | 単方向/双方向 |
以降では、各フレームワークの特徴を簡潔にまとめる
React
VueやAngularと比較すると人気度が高く、エコシステムが豊富な点が特徴
単方向データフロー
Reactでは明示的な状態更新(useState)が必要
VueやAngularは双方向データフローも単方向データフローも実装可能
単方向データフローと双方向データフロー
説明 | 特徴 | |
---|---|---|
単方向データフロー | データは一方向にのみ流れる、状態の変更は必ず決められたアクションを通じて行う | コード量多、状態管理が明確 |
双方向データフロー | データとUIが自動的に同期、状態の変更が双方向に伝播 | コード量少、直感的な実装 |
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>{count}</p>
<button onClick={() => setCount(count + 1)}>
Increment
</button>
</div>
);
}
豊富なエコシステム
- Redux/MobX (状態管理)
- React Router(ルーティング管理)
- Next.js/Remix(フレームワーク)
- React Native(ネイティブアプリ用)
- Material-UI/Chakra UI(コンポーネント)
Vue.js
Reactと同様に、仮想DOMと状態管理の仕組みあり
部分的に導入可能などの柔軟性が強み、学習コストも低い
レンダリングシステム
- テンプレート構文とJSXの両方をサポート
- テンプレート構文とJSXの共存も可能
テンプレート構文:HTMLライクで直感的
<template>
<div>
<!-- データバインディング -->
<h1>{{ title }}</h1>
<!-- ディレクティブ -->
<ul>
<li v-for="item in items" :key="item.id">
{{ item.name }}
</li>
</ul>
<!-- イベントハンドリング -->
<button @click="handleClick">Click me</button>
</div>
</template>
JSX:JSの柔軟性を活かした記述が可能
<script>
export default {
render() {
return (
<div>
<h1>{this.title}</h1>
<ul>
{this.items.map(item => (
<li key={item.id}>{item.name}</li>
))}
</ul>
<button onClick={this.handleClick}>Click me</button>
</div>
)
}
}
</script>
ディレクティブシステム
DOM要素に特別な動作を付与するための強力な機能
- データバインディング系
<template>
<!-- テキストバインディング -->
<span>{{ message }}</span>
<!-- 属性バインディング -->
<img :src="imageUrl">
<!-- クラスバインディング -->
<div :class="{ active: isActive }">
</template>
- 条件分岐とループ
<template>
<!-- 条件分岐 -->
<div v-if="isAdmin">管理者メニュー</div>
<div v-else-if="isUser">ユーザーメニュー</div>
<div v-else>ゲストメニュー</div>
<!-- ループ処理 -->
<ul>
<li v-for="item in items" :key="item.id">
{{ item.name }}
</li>
</ul>
</template>
3.イベント処理
<template>
<!-- 基本的なイベント -->
<button @click="handleClick">クリック</button>
<!-- イベント修飾子 -->
<form @submit.prevent="onSubmit">
<input @keyup.enter="onEnter">
</form>
</template>
- フォーム制御
<template>
<!-- 双方向バインディング -->
<input v-model="username">
<!-- 修飾子付き -->
<input v-model.trim="email">
<input v-model.number="age">
</template>
カスタムディレクティブ
DOM要素に対して独自の振る舞いを追加できる強力な機能
app.directive('my-directive', {
// フック関数
mounted(el, binding) {
// 要素がDOMに挿入された時の処理
},
updated(el, binding) {
// コンポーネントが更新された時の処理
}
})
ミックスイン機能
コンポーネント間で再利用可能なロジックを共有する機能
基本的なミックスイン
export const loggerMixin = {
created() {
console.log('コンポーネントが作成されました')
},
methods: {
log(message) {
console.log(message)
}
}
}
ミックスイン使用例
<script>
import { loggerMixin } from '../mixins/loggerMixin'
export default {
mixins: [loggerMixin],
methods: {
fetchUsers() {
this.log('ユーザー取得開始')
// ユーザー取得処理
}
}
}
</script>
Angular
Angular.jsは古い名称なので最新のものはAngular
大規模チーム向け、学習コストは高いが堅牢性の高いシステムにしたい場合に最適
依存性注入(DI)システム
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
@Injectable({
providedIn: 'root',
})
export class DataService {
constructor(private http: HttpClient) { }
fetchData() {
return this.http.get('https://api.example.com/data');
}
}
RxJSの完全統合
- RxJSとは
- 非同期操作やイベントの処理などリアクティブプログラミングを実現するためのライブラリ
export class DataComponent {
data$ = this.http.get('/api/data').pipe(
map(response => response.data),
catchError(error => of([])),
shareReplay(1)
);
constructor(private http: HttpClient) {}
}
AOTコンパイル
- AOTコンパイル
- (Ahead-of-Time)コンパイルのこと
- アプリケーションを実行する前にコンパイルすること
ng build --prod
モジュールシステム
アプリケーションを論理的な単位に分割して整理する仕組み
下記の種類が存在する
- ルートモジュール
- アプリケーションの起点
- 機能モジュール
- 特定の機能をカプセル化
- 共有モジュール
- 再利用可能なコンポーネント
- コアモジュール
- シングルトンサービス
- ルーティングモジュール
- ナビゲーション制御
@NgModule({
declarations: [FeatureComponent],
imports: [
CommonModule,
FeatureRoutingModule
],
providers: [FeatureService]
})
export class FeatureModule { }
フォーム処理システム
リアクティブフォーム
、テンプレート駆動フォーム
の2種類ある
バリデーションなど複雑なフォームにも対応可能
export class UserFormComponent {
userForm = this.fb.group({
name: ['', [Validators.required]],
email: ['', [Validators.email]],
age: [null, [Validators.min(0)]]
});
constructor(private fb: FormBuilder) {}
}
ゾーン(Zone.js)
Angularアプリの変更検知を自動的に行う
非同期処理の監視
、変更検知
のトリガーなど
@Component({
selector: 'app-example',
template: `
<div>{{ data }}</div>
<button (click)="updateData()">Update</button>
`
})
export class ExampleComponent {
data = 'initial';
updateData() {
// この変更は自動的に検知される
this.data = 'updated';
}
}
Discussion