Backbone.js の開発環境を構築し MVCパターンで書いてみた


これまで個人的に AngularJS、Knockout.js、Vue.js そのほか諸々を試してきたのですが、まだ [Backbone.js](http://backbonejs.org/) はちゃんと触ってないな〜ということで試してみました。

今回は Yeoman の [backbone](https://github.com/yeoman/generator-backbone) ジェネレータを利用しました。ジェネレータを利用した理由としては、初めてなのでベストプラクティスっぽい構成をまずは知りたかったのと、CoffeeScript で書くための環境を、手軽に作りたかったからです。

> Yeomanの ジェネレータを利用すると Grunt/Bower まわりの環境がある程度そろうので楽です。

## 環境構築

### 前提

- 手元の OS：`Mac OS X 10.9.5 (Mavericks)`
- Yeoman、Grunt、Bower を使います。もしまだインストールしていない場合、こちらの投稿を参考にインストールしてください。
	- [Mac で Yeoman の環境を構築する手順](http://qiita.com/hkusu/items/0d2ed38c47b73201f4c2)
- bacbone ジェネレータをインストールしていない場合は、次のようにインストール。

	```shell:
	$ npm install -g generator-backbone
	```

- 結果的に今回、利用したツール/ライブラリ郡の細かいバージョンは次のとおり。
	- Yeoman：`1.3.3`
	- Yeoman の generator-backbone：`0.3.3`
	- Backbone.js：`1.1.2`
	- JQuery：`2.1.1` .. Backbone.js はこれに依存している
	- Lo-Dash：`2.4.1` .. Backbone.js はこれに依存している

### 手順

- ジェネレータを起動します。

	```shell:
	$ yo backbone
	```

	![スクリーンショット 2014-11-17 12.32.57.png](https://qiita-image-store.s3.amazonaws.com/0/35733/6891b48e-191a-5578-ee74-6ac553d5cb12.png "スクリーンショット 2014-11-17 12.32.57.png")

	今回は CoffScript だけを選択しました。

- 雛型生成がおわった直後は、JavaScriptライブラリ類が入っていないようなので、更に Bower でインストールします。

	```shell:
	$ bower install
	```

- 次のようにファイルが展開されました。

	![スクリーンショット 2014-11-17 12.41.01.png](https://qiita-image-store.s3.amazonaws.com/0/35733/81f74a93-17a0-bd21-08f7-a4fe32b206ee.png "スクリーンショット 2014-11-17 12.41.01.png")

- ここで動作確認します。

	```shell:
	$ grunt serve
	```

	![スクリーンショット 2014-11-17 12.42.04.png](https://qiita-image-store.s3.amazonaws.com/0/35733/8747a68e-fafd-ea9a-5a14-5647d13cd6e4.png "スクリーンショット 2014-11-17 12.42.04.png")

## Backbone.js で書いてみる

Backbone.js の主要なコンポーネントは `Model` `Collection` `View` `Router` です。この Yeoman の環境では、それぞれ次のような生成コマンドが用意されています。

```shell:
$ yo backbone:model hoge
$ yo backbone:collection hoge
$ yo backbone:view hoge
$ yo backbone:router hoge
```

> そのほか backbone ジェネレータの説明は、GitHubで確認できます。
> ⇒ https://github.com/yeoman/generator-backbone

今回は最低限で `Model` と `View` だけ使ってみます。

```shell:
$ yo backbone:model hoge_model
$ yo backbone:view hoge_view
```

すると、次のように `Model` と `View` の雛型が生成されます。

![スクリーンショット 2014-11-17 17.26.39.png](https://qiita-image-store.s3.amazonaws.com/0/35733/b3c54656-ea07-96ab-533a-16f072e9ed1a.png "スクリーンショット 2014-11-17 17.26.39.png")

こっからソースを書いていきます。結果からいうと、こういう簡単なものを作ります。

![スクリーンショット 2014-11-17 16.26.40.png](https://qiita-image-store.s3.amazonaws.com/0/35733/bb1def58-19c7-96fc-4846-5c8197738999.png "スクリーンショット 2014-11-17 16.26.40.png")

Backbone.js の思想？に従って **ユーザ入力はコントローラ経由でモデルへ反映し、ビューはモデルの変更を検知して自身の表示内容を変更** という風にします。

> 今回のソースは GitHub にも置いてあります。
> ⇒ https://github.com/hkusu/Backbone.js_demo

### Model の作成

ほぼ雛型のままなのですが、今回あつかう２つの値 `name` `age` のデフォルト値(空)を設定します。

```coffeescript:app/scripts/models/hoge_model.coffee
'use strict';

class BackboneTest.Models.HogeModel extends Backbone.Model
  url: ''

  initialize: () ->

  defaults:
    name: ""
    age: ""

  validate: (attrs, options) ->

  parse: (response, options) ->
    response

```

### View と テンプレートの作成

ややこしいのですが、Backbone.js でいう `View` は、他のフレームワークでいいう **コントローラ** です。

まずテンプレートから。表示部分は `<$= ほげほげ %>` で変数を記載します。入力部分は、あとで jQuery で `input` タグを拾うのでソース上は何もしませんが、本当は id や class を指定した方が良いかも。

```coffeescript:app/scripts/templates/hoge_view.ejs
<p>
  名前：<%= name %>
</p>
<p>
  年齢：<%= age %>
</p>

<hr>

<p>
  新しい年齢：<input type="text" />
  <br>
  <font size="1px" color="orange">
    Model へ反映 ⇨ 自動的に View へ反映されるデモです。<br>
    ただリアルタイムデータバインドが聞いてないのでカーソル外してください..</font>
</p>
```

次は `View`（他のフレームワークでいうコントローラ）を書きます。追加したのは `event` で `input` タグの変更を拾うところ（= コントローラの役割）と、`inputCange` メソッドです。

注意すべきことは、

- `inputCange` メソッドでは、Model を更新しています。
- `listenTo` で Model の変更を監視し、変更があった場合は再描写しています。

```coffeescript:app/scripts/views/hoge_view.coffee
'use strict';

class BackboneTest.Views.HogeView extends Backbone.View

  template: JST['app/scripts/templates/hoge_view.ejs']

  tagName: 'div'

  id: ''

  className: ''

  events:
    "change input": "inputCange"

  inputCange: () ->
      @model.set("age", @$("input").val())

  initialize: () ->
    @listenTo @model, 'change', @render

  render: () ->
    @$el.html @template(@model.toJSON())
```

## 全体を統合する

この View をブラウザへ表示するために、`index.html` に div タグを仕込み、

```html:app/index.html
〜

<div id="hoge"></div>

〜
```

最後に `main.coffee` で、

- Model と View をインスタンス化
- View を `index.html` へ差し込み
- View と Model の紐付け

をします。

```coffeescript:app/scripts/main.coffee
window.BackboneTest =
  Models: {}
  Collections: {}
  Views: {}
  Routers: {}
  init: ->
    'use strict'

    model = new @Models.HogeModel(
        name: "山田 太郎"
        age: 31
    )

    view = new @Views.HogeView(
      el: $("#hoge")
      model: model
    )

    view.render()

    return

$ ->
  'use strict'
  BackboneTest.init();
```

デモはこちらに置きました（GitHub Pages）。
⇒ http://hkusu.github.io/Backbone.js_demo/dist/

## おわりに（Backbone.js に対する感想）

前々から聞いてはいたのですが、一つのことを実現するのに色々なところに記述を書く必要があるなあと感じた反面、フレームワークで縛られるので、実現方法が統一されるなと。

また、モデルがしっかり書けるな、と思いました。サーバサイドのモデルと連携 ＆ かっちりデータ構造が定義されるシステムに向いているのでは。

View まわりは単体だと少し弱い(jQueryを直接いじってる気分になる)と思ったのですが、他のライブラリと組み合わせたり、[Marionette.js](http://marionettejs.com/) を使えばそこは解消されるんだろうなと思いました。