Choo.js: Fun functional programming

世の中では、VueやReactなどのイケイケドンドンFFWが大人気だが、それ以外にも様々なフロントエンド・フレームワークがひっそりと息をしている。ということで、今回は[Choo.js](https://choo.io/)という4kbの小さなフレームワークの紹介をしようと思う。

# Choo.jsとは :steam_locomotive::train::train::train::train::train:
GithubのREADMEに`A 4kb framework for creating sturdy frontend applications`と書いてあるように、Choo.jsは小さく始めながらも堅牢なフロントエンド・アプリケーションを作れるような機能を提供するフレームワークだ。

Choo.js自体はたった200行程度のソースコードから成り立っていて、アプリケーションが提供しているルーティングの機能や、EventEmitterなどは、[nanorouter](https://github.com/choojs/nanorouter)や[nanobus](https://github.com/choojs/nanobus)などのいくつかの異なる小さなモジュールの組み合わせから成り立っている。Choo.jsのエコシステムの一部として、これらのモジュールはFramework-agnostic[^1]であるということを主眼に置いており、できるだけ粗結合で依存の少ないコードベースであるということを謳っている。

# 使ってみる
チュートリアルということでWebpackBinをここでは使う。
以下のURLをブラウザから開けば実際に動かしたりソースコードをいじったりできる。
https://www.webpackbin.com/bins/-L-V4Vk0BYQ12x5CR7Fs

ちなみにこんなアプリケーションを作ります
![Peek 2017-12-04 15-37.gif](https://qiita-image-store.s3.amazonaws.com/0/18973/02a25f57-7706-fa81-be66-4a925a9cd4f3.gif)

### **HTML**
HTMLは以下の数十行だけでよい。メインの実装はJS側で行う。

```html:index.html
<!doctype html>
<html>
  <head>
    <meta charset="utf-8"/>
  </head>
  <body>
    <div id="app"></div>
    <script src="main.js"></script>
  </body>
</html>
```

### **JS**
JS側で実際にChooを使ったアプリケーションの構築を行っていく

Choo.jsには**ビュー**と**ストア**という２つの概念しか無いと行っても、ほぼほぼ差し支えない。

ビュー関数はES6のタグテンプレート・リテラルを用いてDOMを構造を返すだけの純粋な関数だ。その定義の中でのボタンのクリックやなんらかの変更を、引数として受け取っている`emit`関数に渡して呼び出すことで、ストアが変更をキャッチできるようになる。

```js:ビュー関数
//
// ◎ビュー定義関数
// state（ステート）を受け取り、DOMを返す関数としてビューを定義
//
function mainView (state, emit) {

  // ES6のタグテンプレート・リテラルでDOM構造を定義できる
  return html`
    <div>
      <h1>Title: ${state.title}</h1>
      <input type="text" value="${state.title}" oninput=${update} />
    </div>
  `

  // 引数から受け取っているemit関数を呼び出すことでストアの更新をトリガできる
  function update(e) {
    emit('update', e.target.value)
  }
}
```
ストアはEventEmitterのインスタンスとして`emitter`を受け取るので、それに対してリスナを登録してやるだけでよい。`render`をemitすることでビューにステートを反映できる。

また、自前でイベントを登録する以外にも、DOMがマウントされたときに呼び出される`'DOMContentLoaded'`などのライフサイクル・ハンドラも用意されている[^3]

[^3]: https://github.com/choojs/choo#events

```js:ストア関数
//
// @ストア定義関数
// 引数で受け取るstateとemitterに対してストアとイベントハンドラを定義する
//
function titleStore (state, emitter) {

  // stateに属性を生やす形でデフォルト・ステートを定義
  state.title = 'Not quite set yet'
  
  // ビュー関数からemitでトリガされるイベントハンドラを定義
  emitter.on('update', function (title) {
    state.title = title

    // ビューの更新を実行
    emitter.emit('render')
  })
}
```

これらの一連の流れをひとつにまとめると、このような感じになる。

```js:main.js
const html = require("choo/html");
const choo = require("choo");
const app = choo();

function mainView (state, emit) {
  return html`
    <div>
      <h1>Title: ${state.title}</h1>
      <input type="text" value="${state.title}" oninput=${update} />
    </div>
  `

  function update (e) {
    emit('update', e.target.value)
  }
}

function titleStore (state, emitter) {
  state.title = 'Not quite set yet'
  
  emitter.on('update', function (newTitle) {
    state.title = newTitle
    emitter.emit('render')
  })
}

app.use(titleStore)
app.route('/', mainView)
app.mount('#app')
```

#### **余談: ミドルウェア**
上のコードではストアを`app.use()`を使って登録しているが、厳密にはchoo.jsのストアはミドルウェアのひとつとして作られているに過ぎない。Choo.jsでは、`state`と`emitter`を受け取る関数であれば、なんでもuseメソッドを使って登録することができる。

```js:ミドルウェア例
function YourMiddleware() {
  return function (state, emitter) {
    // do your work here
  }
}

app.use(YourMiddleware());
```
公式で容易されている[choo-devtools](https://www.npmjs.com/package/choo-devtools)などのモジュールも同じような形で利用できるようになっている

```js:choo-devtool
const choo = require('choo');
const devtools = require('choo-devtools');

const app = choo()
if (process.env.NODE_ENV !== 'production') {
  app.use(devtools())
}
app.mount('body')
```

# choo-cliを使う
そもそもフロントエンドは開発環境を構築するのがダルい。Webpackの設定ファイルなど書きたくない。そんな人のためにchoo-cliというスキャフォルディングツールが用意されているので、それを使って新しくプロジェクトを生成することもできる。

```bash:choo-cliでアプリケーションを作成
$ choo new example-choo-app
$ cd example-choo-app
```
choo-cliで生成されるアプリケーションにはデフォルトでbankai[^2]という、フロントエンドアプリケーションのバンドリングから開発用サーバの起動までをワンストップでやってくれる便利なツールが追加され、以下のようなスクリプトが使えるようになる

```bash:開発用サーバを起動
$ npm start
```

```bash:アプリケーションをビルド
$ npm run build
```

# まとめ
個人的によさそうだなと思ったところは以下の３つ

#### コードベースが小さく、困ってもソースを読めばなんとかなる
- フレームワークの実装があまりにも巨大すぎると、issueやStackoverflowにないような困りごとを自分でソースコードを読んで解決するのはなかなか難しかったり、解決できてもPRの粒度を小さく保つのが難しくなったりする。加えて、OSSなソースコードリーディングにも最適。

#### ビューが基本的に関数というアプローチ
- Choo.jsにおいて、ビューはクラスのインスタンスでもなんでもなく、単なるDOM構造を返すだけの関数でしか無い。つまり常にステートレスで、責務が明確に分離されている。純粋な関数として実装されるのでテストも容易な上、DIされるのはEventEmitterだけなのでモックも簡単。

#### 最低限のEventEmitterだけが用意されている
- Choo.jsにはビューとストアという２つのレイヤしか用意されていない。そして、ストアは単なるEventEmitterでしかない。ここに自前でFluxアーキテクチャを実装してもいいし、これをこのままつかってもよい。データ・フローの実装への自由度が高い。

ぜひ気になったら触ってみてください :muscle: 

[^1]: 特定のフレームワークに依存しないこと
[^2]: https://github.com/choojs/bankai