10
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

世の中では、VueやReactなどのイケイケドンドンFFWが大人気だが、それ以外にも様々なフロントエンド・フレームワークがひっそりと息をしている。ということで、今回はChoo.jsという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などは、nanorouternanobusなどのいくつかの異なる小さなモジュールの組み合わせから成り立っている。Choo.jsのエコシステムの一部として、これらのモジュールはFramework-agnostic1であるということを主眼に置いており、できるだけ粗結合で依存の少ないコードベースであるということを謳っている。

使ってみる

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

ちなみにこんなアプリケーションを作ります
Peek 2017-12-04 15-37.gif

HTML

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

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にはビューストアという2つの概念しか無いと行っても、ほぼほぼ差し支えない。

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

ビュー関数
//
// ◎ビュー定義関数
// 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'などのライフサイクル・ハンドラも用意されている2

ストア関数
//
// @ストア定義関数
// 引数で受け取る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')
  })
}

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

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では、stateemitterを受け取る関数であれば、なんでもuseメソッドを使って登録することができる。

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

app.use(YourMiddleware());

公式で容易されているchoo-devtoolsなどのモジュールも同じような形で利用できるようになっている

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というスキャフォルディングツールが用意されているので、それを使って新しくプロジェクトを生成することもできる。

choo-cliでアプリケーションを作成
$ choo new example-choo-app
$ cd example-choo-app

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

開発用サーバを起動
$ npm start
アプリケーションをビルド
$ npm run build

まとめ

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

コードベースが小さく、困ってもソースを読めばなんとかなる

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

ビューが基本的に関数というアプローチ

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

最低限のEventEmitterだけが用意されている

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

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

  1. 特定のフレームワークに依存しないこと

  2. https://github.com/choojs/choo#events

  3. https://github.com/choojs/bankai

10
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?