をつくるとどうなるのかと思い作ってみた。
jqueryからいきなりreact+flux+es6に行くとあらゆるものが変わってしまうので、
こういう試しも比較する上で面白いのではないかと。
つくるものの要件
クッキークリッカー的にカウンターが
- ボタンをクリックしたらカウンター+1
- 1秒ごとに+1
- カウンターの値を30消費してlevel2にアップデートできる
- level2だとカウンターが2づつ増える
スクショ
HTML
htmlは以下のような感じ
jqueryとオブジェクトの更新に_.extend()を使いたいのでunderscore.jsを読み込む
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JQueryでFlux</title>
</head>
<body>
<div class="wrapper">
<div><label>Cookie: </label><span id="value">0</span></div>
<div>
<button id="button">Click!</button>
</div>
<div>
level: <span id="level">1</span>
</div>
<div>クッキーを30使いアップデートする <button id="update">Update!</button></div>
</div>
<script src="https://code.jquery.com/jquery-3.1.0.min.js" integrity="sha256-cCueBR6CsyA4/9szpPfrX3s49M9vUU5BgtiJj06wt/s=" crossorigin="anonymous"></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js'></script>
<script src='/js/script.js'></script>
</body>
</html>
非Fluxの雑な例
script.js
$(function() {
$('#button').on('click', function(e) {
increaseCount();
});
setInterval(function() {
increaseCount();
}, 1000);
function increaseCount() {
var newValue = parseInt($('#value').html(), 10) + 1;
$('#value').html(newValue);
}
});
Flux
Fluxの場合
上記との違い
storeを定義する。
つまりDOMに状態を持たせない。
さらに言うとDOMから値を取得しない。
ViewはActionを発行する
適当にJqueryのカスタムイベントを発行する
Actionを受け取る
Actionを受け取り、storeを更新。
更新したら必ずstoreのchangeイベントを発行する。
Viewを更新する。
storeのchangeイベントをトリガーにhtmlを書き換える
script.js
$(function() {
// Store定義
var store = {
cookie: 0,
level: 1
};
// Storeの値は必ずこの関数から取得
function getStore(key) {
return store[key];
}
// Storeの値は必ずこの関数を通して更新
function updateStore(newState) {
// 現在のStoreに新しい値をマージした新オブジェクトを作成、代入する
store = _.extend({}, store, newState);
// update時にchangeイベントを発行する。changeイベントを発行するのはここのみ
$(window).trigger('store:change');
}
// ViewはStore(store:changeイベント)を購読する Viewの更新はここでのみ行う
$(window).on('store:change', function() {
$('#value').html(getStore('cookie'));
$('#level').html(getStore('level'));
});
// ここまでがフレームワークと言えるものかもしれない
// View(html)からActionの発行(Actionをtriggerするのがこのブロックの仕事)
$('#button').on('click', function() {
$(window).trigger('action:increase');
});
setInterval(function() {
$(window).trigger('action:increase');
}, 1000);
$('#update').on('click', function() {
$(window).trigger('action:requestUpdate');
});
// Actionを受け取ってupdateStore()するのがこのブロックの仕事
$(window).on('action:increase', function() {
increaseCount();
});
function increaseCount() {
var amount = 1;
if(getStore('level') === 2) {
amount = 2;
}
updateStore({cookie: getStore('cookie') + amount});
}
function decreaseCount(amount) {
updateStore({cookie: getStore('cookie') - amount});
}
$(window).on('action:requestUpdate', function() {
if(getStore('level') === 1 && getStore('cookie') > 30) {
decreaseCount(30);
increaseLevel();
}
});
function increaseLevel() {
updateStore({level: getStore('level') + 1});
}
});