シンプルな MVVM ライブラリ『Vue.js』の環境構築と使ってみた感想


AngularJS と Knockout.js を比較して、個人的には Knockout.js のような **ViewModel** を採用しているものの方が分りやすい（あと Rails に薄く被せる時とか）と思い、同じように ViewModel を採用している **Vue.js** を試してみました。

> Vue.js の公式ベージ ⇒ http://vuejs.org/

ただ Knockout.js と違いモダンブラウザでないと使えないようです。ただ次期の AngularJS もレガシーブラウザを捨てるようですし、それはいいかな。

![yyx990803_vue.png](https://qiita-image-store.s3.amazonaws.com/0/35733/e5dfe7ca-74d8-8ae0-b402-60950f535dff.png "yyx990803_vue.png")

> https://github.com/yyx990803/vue から引用。

今回 Vue.js を試すにあたり、ライブラリを CDN から読みこんでもよかったのですが、ある程度ちゃんとやるために手元に環境を構築しました。

## 環境構築

Yeoman 公式の [webapp](https://github.com/yeoman/generator-webapp) ジェネレータを元に、Bower で Vue.js をインストールしました。スクリプトは CoffeeScript で書くのが個人的には楽なので、それも導入します。

> `webapp` ジェネレータを利用すると Grunt/Bower まわりの環境が一発で揃うので楽です。
> ただ、商用向けなど本格的に使うなら、ある程度は知識が必要となり、またやりにくい部分も出てくるとは思います。


### 前提

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

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

### 手順

- ジェネレータを起動。この際、`--coffee` をつけます。

	```shell:
	$ yo webapp --coffee
	```

- (少しはデザインが欲しいので)Bootstrap だけ選択します。

	![vue2_—_node_—_158×24.png](https://qiita-image-store.s3.amazonaws.com/0/35733/988581d1-337b-c450-add1-df2fac3866a6.png "vue2_—_node_—_158×24.png")

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

	![app_index_html_-_vue_-____WebstormProjects_vue_.png](https://qiita-image-store.s3.amazonaws.com/0/35733/b99036f3-d796-6b93-d4c5-29c71e64f318.png "app_index_html_-_vue_-____WebstormProjects_vue_.png")

- Bower で Vue.js と、一応 JQuery もインストールします。

	```shell:
	$ bower install vue --save
	$ bower install jquery --save
	```

	- 結果の `bower.json` は次のとおり。

		```javascript:bower.json
		{
		  "name": "vue",
		  "private": true,
		  "dependencies": {
		    "bootstrap": "~3.2.0",
		    "vue": "~0.10.6",
		    "jquery": "~2.1.1"
		  }
		}
		```

- 次のコマンドを打つと、`index.html` が自動で編集され、Bower でインストールしたライブラリを読む込むようになります。

	```shell:
	$ grunt wiredep
	```

	> 前は `$grunt bowerInstall` だったのに、変わったのかな.. ジェネレータのバージョンによって変わるかもなので、正確なコマンドは `Gruntfile.js` を見て調べてください。

	- 結果の `index.html` は次のとおり。

		```html:app/index.html
	    <!-- build:js(.) scripts/vendor.js -->
	    <!-- bower:js -->
	    <script src="bower_components/jquery/dist/jquery.js"></script>
	    <script src="bower_components/vue/dist/vue.js"></script>
	    <!-- endbower -->
	    <!-- endbuild -->
		```

- いったん動作確認してみます。

	```shell:
	$ grunt serve
	```

	![vue.png](https://qiita-image-store.s3.amazonaws.com/0/35733/fa36e1fe-3071-af1f-89a2-76c3c9a4fecb.png "vue.png")

もしリリース用にビルドする場合は、`$ grunt build` とします。すると `dist` フォルダ下に、minify や Cofffee 等のコンパイルされたファイルが展開されます。

この手順で作った環境のソースコードは、GitHub にも置きました。
⇒ https://github.com/hkusu/Vue.js_demo

## Vue.js を試してみる

### ViewModel を書く

軽く公式ページを確認した感じだと、`var hoge = new Vue()` で VewModel を定義し、引数にはオブジェクト {..} を渡すようです。

オブジェクトの `el`プロパティには、バインド対象とする View 上の div エリアの id を指定し、利用するデータは `data` プロパティ、メソッドは `methods` プロパティで定義します。

メソッドの中でデータを利用する場合は、`this.hoge`（Coffeeの場合は @hoge）のように参照します。

```coffeescript:app/scripts/main.coffee
$ ->
  demo = new Vue(
    el: "#demo"
    data:
      firstName: "太郎"
      lastName: "山田"
      count: 0
      #message: ""  ← 初期値を設定しないでいいなら省ける
    methods:
      execute: ->
        @message = "実行しました"
        return
  )

  return
```

この例だと、`demo = new Vue()` のように ViewModel インスタンスへの参照を変数にいれたけど、何かに使えるのだろうか？（コンポーネントとして再利用できる？）


### View を書く

単純にデータを扱うなら `{{..}}` の中に変数を書けばいいです。inputフォームの内容とデータを紐づけるなら `v-model` ディレクティブで、イベントは `v-on` で取得できます。

ViewModel に定義したメソッドは `v-on="click: hoge_method"` のようにして呼び出せます。

```html:app/index.html
<div id="demo">

  <h4><font color="orange">データバインドの例</font></h4>

  <p>
    {{lastName}} {{firstName}}
  </p>
  <p>
    <input v-model="lastName"/> <input v-model="firstName"/>
  </p>

  <hr>

  <h4><font color="orange">クリックイベントの例</font></h4>

  <p>
    {{count}}
  </p>
  <p>
    <button class="btn btn-primary" v-on="click: count++">カウント</button>
  </p>

  <hr>

  <h4><font color="orange">メソッド実行の例</font></h4>

  <p>
    {{message}}
  </p>
  <p>
    <button class="btn btn-primary" v-on="click: execute">実行</button>
  </p>

</div>
```

### 動作確認

こんな感じです。

![vue 2 20.13.48.png](https://qiita-image-store.s3.amazonaws.com/0/35733/217edfb0-5ca5-1710-360d-61d767abc686.png "vue 2 20.13.48.png")

こちら（GitHub Pages）にデモを置きました。※IE8とかだと動きません。
⇒ http://hkusu.github.io/Vue.js_demo/dist/

## 感想

とても良い印象です。いまのところ悪いところは無いです。とくに気に入ったのは、

- View で `{{hoge}}` のように書けば、これだけで（ViewModel 側で変数を定義しなくても）Vue.js の 監視対象に追加されるので楽ちん！
	- デザイナーさんも扱い易いのではないでしょうか。
- Knockout.js のように View に `"data-bind" => "text: hoge"` というように長々と書かなくていいので、View の見た目がごちゃごちゃしない。
- ViewMode 側で div エリアを指定してバインドできる。View 側は特別なことをしなくてよい。
	- AngularJS のように `ng-controller=` とかで View を汚さなくてよい。
- ViewModel の構造が分りやすい。
	- データは `data` プロパティ、メソッドは `methods` プロパティに書くように縛られているので、一目で何をやっているかが分る。

ひとことで言うと「分りやすい」です。もし大規模でつかうなら、他のフレームワークと組みあわせればいいのかな？

AngularJS はフルスタックであったり DI ベースであったりと、単純には比較できない（用途や目的が違う）のですが、少なくとも Knockout.js よりはいい感じがします。

次期バージョンの開発も進んでいるようですし（個人的には URLのルーティング機能は欲しい）、期待したいと思います。