ほんとかどうかわかんない話(Redux, Flux, MVC, MVVM)
`Flux`と`Redux`は、JavaScriptアプリケーションのための状態管理パターン及びライブラリです。
Flux: Facebookによって開発された、クライアントサイドWebアプリケーションのためのアーキテクチャパターンです。Fluxは、単一方向のデータフローを採用しており、アプリケーションの状態管理をより予測可能にします。主要な概念は`Actions`(アクション)、`Dispatcher`(ディスパッチャー)、`Stores`(ストア)、および`Views`(ビュー)です。アクションがディスパッチャーによってストアに送られ、ビューが更新されます。
Redux: Fluxを基にして、よりシンプルで小さなライブラリとして開発されました。Reduxは、アプリケーション全体の状態を単一のストア(状態ツリー)で管理します。Reduxの主要な原則は、状態が読み取り専用であり、状態を変更するにはアクションを発行する必要があること、そしてアクションは純粋な関数(レデューサー)によって処理されることです。Reduxは特にReactと一緒によく使用されますが、他のUIライブラリやフレームワークとも組み合わせることができます。
どちらも、大規模なアプリケーションの状態を効果的に管理し、コンポーネント間でのデータの流れを整理することに役立ちます。ReduxはFluxよりも人気があり、広範囲にわたるエコシステムとツールセットを持っています。
Redux
以下に、Reduxを用いた基本的な例を示します。この例では、単純なカウンターアプリケーションを作成します。Reduxの三つの主要な要素(アクション、レデューサー、ストア)を使用して、アプリケーションの状態(この場合は数値)を管理します。
ステップ1: アクションの定義
まず、アクションタイプとアクションクリエーターを定義します。
// アクションタイプの定義
const INCREMENT = 'INCREMENT';
const DECREMENT = 'DECREMENT';
// アクションクリエーター
const increment = () => ({ type: INCREMENT });
const decrement = () => ({ type: DECREMENT });
ステップ2: レデューサーの作成
次に、アプリケーションの状態を変更するためのレデューサー関数を作成します。
// 初期状態
const initialState = {
count: 0
};
// レデューサー関数
function counterReducer(state = initialState, action) {
switch (action.type) {
case INCREMENT:
return { count: state.count + 1 };
case DECREMENT:
return { count: state.count - 1 };
default:
return state;
}
}
ステップ3: ストアの作成
最後に、Reduxストアを作成します。ストアはアプリケーションの状態を保持し、レデューサーを使用して状態の更新を行います。
import { createStore } from 'redux';
// ストアの作成
const store = createStore(counterReducer);
// ストアの状態が変更されたときのリスナー
store.subscribe(() => console.log(store.getState()));
// アクションのディスパッチ
store.dispatch(increment()); // カウントを1増やす
store.dispatch(increment()); // もう一度1増やす
store.dispatch(decrement()); // カウントを1減らす
このコードは、カウンターの値を増減させる基本的なReduxの例です。実際のアプリケーションでは、ReactなどのUIフレームワークと組み合わせて、ユーザーインターフェースにこれらの状態の変化を反映させます。また、実際のアプリケーションでは、より複雑な状態管理や非同期処理、ミドルウェアなどの概念も取り入れることが一般的です。
Reduxの仕組み
Reduxの内部実装の仕組みをシンプルなJavaScriptで示し、その後にFluxの基本的な概念を説明します。
Reduxの内部実装のサンプル
Reduxは主に三つの部分から成り立っています:ストア、レデューサー、アクション。以下の例では、これらの部分を独自に実装しています。
1. アクションとアクションクリエーター
// アクションタイプ
const INCREMENT = 'INCREMENT';
const DECREMENT = 'DECREMENT';
// アクションクリエーター
function createActionIncrement() {
return { type: INCREMENT };
}
function createActionDecrement() {
return { type: DECREMENT };
}
2. レデューサー
function counterReducer(state = 0, action) {
switch (action.type) {
case INCREMENT:
return state + 1;
case DECREMENT:
return state - 1;
default:
return state;
}
}
3. ストア
function createStore(reducer) {
let state;
let listeners = [];
function getState() {
return state;
}
function dispatch(action) {
state = reducer(state, action);
listeners.forEach(listener => listener());
}
function subscribe(listener) {
listeners.push(listener);
return function unsubscribe() {
listeners = listeners.filter(l => l !== listener);
};
}
dispatch({}); // 初期状態を設定するためのダミーのディスパッチ
return { getState, dispatch, subscribe };
}
// ストアの作成
const store = createStore(counterReducer);
// リスナーの登録
store.subscribe(() => console.log(store.getState()));
// アクションのディスパッチ
store.dispatch(createActionIncrement());
store.dispatch(createActionIncrement());
store.dispatch(createActionDecrement());
Fluxの基本的な概念
Fluxは単一方向データフローを持つアーキテクチャパターンです。主要な部分は以下の通りです:
アクション: ユーザーの操作やサーバーからのイベントなど、システムに何かが起きたことを表すシンプルなオブジェクトです。
ディスパッチャー: アクションを受け取り、登録されたコールバックを呼び出します。
ストア: アプリケーションの状態を保持し、ディスパッチャーからのアクションに応じてその状態を更新します。
ビュー: ストアの状態に基づいてユーザーインターフェースを更新します。
Fluxの実装例は、Reduxとは異なり、ディスパッチャーとストアがより明示的に分離されている点が特徴です。ただし、Fluxはあくまでパターンであり、その具体的な実装は多岐にわたります。
Fluxアーキテクチャ
dispacherがstoreのハンドラを保持。
ストアにリスナーを作っておき、このリスナーのハンドラが画面描画を担当。 ディスパッチャにアクションを渡すとストアにある内部データが更新されて、続けてストアのリスナー(画面描画)が起動する
Fluxアーキテクチャに基づいたシンプルなJavaScriptの実装をいくつか示します。Fluxは、アクション、ディスパッチャー、ストア、ビューという4つの主要な部分から成ります。ここでは、これらの概念を実装するための簡単な例を紹介します。
1. アクションの定義
まず、アクションタイプとアクションクリエーターを定義します。
// アクションタイプ
const INCREMENT = 'INCREMENT';
const DECREMENT = 'DECREMENT';
// アクションクリエーター
function increment() {
return { type: INCREMENT };
}
function decrement() {
return { type: DECREMENT };
}
2. ディスパッチャーの作成
次に、Fluxディスパッチャーを実装します。
class Dispatcher {
constructor() {
this.handlers = [];
}
dispatch(action) {
this.handlers.forEach(handler => handler(action));
}
register(handler) {
this.handlers.push(handler);
}
}
const dispatcher = new Dispatcher();
3. ストアの実装
ストアはアプリケーションの状態を保持し、ディスパッチャーからのアクションに応じて状態を更新します。
class Store {
constructor(dispatcher) {
this.state = { count: 0 };
dispatcher.register(this.handleActions.bind(this));
}
handleActions(action) {
switch (action.type) {
case INCREMENT:
this.state.count += 1;
this.emitChange();
break;
case DECREMENT:
this.state.count -= 1;
this.emitChange();
break;
default:
// 何もしない
}
}
addChangeListener(callback) {
this.onChange = callback;
}
emitChange() {
if (this.onChange) {
this.onChange(this.state);
}
}
}
const store = new Store(dispatcher);
4. ビューの連携
ビュー(この場合はコンソール出力)がストアの変更を監視し、更新されたときに反応します。
store.addChangeListener((state) => {
console.log(`現在のカウント: ${state.count}`);
});
// アクションのディスパッチ
dispatcher.dispatch(increment());
dispatcher.dispatch(increment());
dispatcher.dispatch(decrement());
この例では、簡単なカウンターアプリケーションをFluxアーキテクチャで実装しています。実際のアプリケーションでは、この構造を拡張し、より複雑なロジックや非同期操作、複数のストアなどを組み込むことが一般的です。また、ビューは通常、Reactなどのライブラリを用いて実装されます。
MVCとMVVM
MVCとMVVMのアーキテクチャパターンの基本的な概念を示すため、シンプルなJavaScriptの例を作成します。この例では、ユーザーがボタンをクリックするとカウンターが増加する、基本的なカウンターアプリケーションを想定します。
MVCのサンプル
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>MVC Example</title>
</head>
<body>
<div id="app">
<button id="incrementButton">Increment</button>
<div id="counterValue">0</div>
</div>
<script>
// Model
class CounterModel {
constructor() {
this.count = 0;
}
increment() {
this.count++;
}
}
// View
class CounterView {
constructor() {
this.incrementButton = document.getElementById('incrementButton');
this.counterValue = document.getElementById('counterValue');
}
setOnClickListener(callback) {
this.incrementButton.addEventListener('click', callback);
}
updateCounter(count) {
this.counterValue.textContent = count;
}
}
// Controller
class CounterController {
constructor(model, view) {
this.model = model;
this.view = view;
this.view.setOnClickListener(() => this.increment());
this.view.updateCounter(this.model.count);
}
increment() {
this.model.increment();
this.view.updateCounter(this.model.count);
}
}
// Initialization
const app = new CounterController(new CounterModel(), new CounterView());
</script>
</body>
</html>
MVVMのサンプル
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>MVVM Example</title>
</head>
<body>
<div id="app">
<button id="incrementButton">Increment</button>
<div id="counterValue">0</div>
</div>
<script>
// Model
class CounterModel {
constructor() {
this.count = 0;
}
increment() {
this.count++;
}
}
// ViewModel
class CounterViewModel {
constructor(model) {
this.model = model;
this.counterValueElement = document.getElementById('counterValue');
this.incrementButton = document.getElementById('incrementButton');
this.incrementButton.addEventListener('click', () => this.increment());
this.updateView();
}
increment() {
this.model.increment();
this.updateView();
}
updateView() {
this.counterValueElement.textContent = this.model.count;
}
}
// Initialization
const app = new CounterViewModel(new CounterModel());
</script>
</body>
</html>
これらの例では、MVCではモデル、ビュー、コントローラーがはっきりと分離されており、ビューとモデルの直接的な通信をコントローラーが仲介しています。一方、MVVMではビューモデルがモデルとビューの間のデータバインディングを担当し、ビューのUIロジックをカプセル化しています。これらのパターンはアプリケーションの設計を整理し、開発とメンテナンスを容易にするためのものです。
この記事が気に入ったらサポートをしてみませんか?