Vue.js少しだけいじった

話題のVue.js、公式サイト見ながらサンプルコード写経したりしてみた。
[Vue.js](http://vuejs.org/)

こういうフレームワーク系ほぼ触ったことなかったので、サンプルコードレベルでも便利さに感動ｗ

以下の記事も参考にしてます。

* [vue.js book](http://blog.koba04.com/vuejs-book/)
* [Vue.js概要？ - Qiita](http://qiita.com/asip2k25/items/bd4bdccd5201d542953c)
* [Vue.jsから手軽に始めるJavaScriptフレームワーク - Qiita](http://qiita.com/icoxfog417/items/49f7301be502bc2ad897)


## 準備
とりあえずサンプルコードいくつか書こうと思ったので、プロジェクトのディレクトリ作成、htmlというディレクトリ下にHTMLを、jsというディレクトリにJavaScript置くことに。

coffeescriptで書きたかったので、ストリーミングビルドシステム[gulp](http://gulpjs.com/)でタスクを作ってcoffeescriptをコンパイルすることに。
（大した規模じゃないのでぶっちゃけ必要ない...）

グローバルにcoffee-script, gulpをインストール。そのあとプロジェクトのディレクトリでライブラリインストール。

```
$ npm install -g gulp coffee-script
```

```
$ npm init
$ npm install --save-dev gulp gulp-coffee coffee-script
```

gulpfileもcoffeeで。

```coffeescript:gulpfile.coffee
gulp   = require 'gulp'
coffee = require 'gulp-coffee'

gulp.task 'coffee', () ->
  gulp.src './coffee/*.coffee'
      .pipe coffee()
      .pipe gulp.dest('./js')
```

いったん手動でコンパイルできれば良いかという感じだったので、上記のようにcoffeeというタスクを一個作って終わり。`gulp coffee`コマンドで、coffeeディレクトリ以下に置いた`*.coffee`ファイルをコンパイルし、jsディレクトリ以下にjsファイルを作る。

今回はやってないが、coffeescriptのファイルを監視して、変更があったら自動でコンパイルするのもgulpで可能なはず。

ディレクトリの構成は以下。

```
.
├── coffee
│   └── hello_vue.coffee
├── gulpfile.coffee
├── html
│   └── hello_vue.html
├── js
│   └── hello_vue.js
├── package.json
└── node_modules
```

## サンプル1 (簡易Todoリスト）

```coffeescript:todo_example.coffee
demo = new Vue
  el: '#demo',
  data: {
    title: 'todos',
    todos: [
      {
        done: true,
        content: 'Learn JavaScript'
      },
      {
        done: false,
        content: 'Learn Vue.js'
      }
    ]
  }
```

```html:todo_example.html
<html>
  <head>
    <title>Vue.js sample</title>

    <style>
    body {
      font-family: Helvetica Neue, Arial, sans-serif;
    }
    li.done {
      text-decoration: line-through;
    }
    </style>
  </head>
  <body>
    <div id="demo">
      <h1>{{title | uppercase}}</h1>
      <ul>
        <li
          v-repeat="todos"
          v-on="click: done = !done"
          class="{{done ? 'done' : ''}}">
          {{content}}
        </li>
      </ul>
    </div>

    <script src="http://cdnjs.cloudflare.com/ajax/libs/vue/0.10.6/vue.min.js"></script>
    <script src="../js/todo_example.js"></script>
  </body>
</html>
```

Vueクラスのインスタンス作成時のオプションとして、`el`と`data`を利用。

* `el`: これで指定したHTML要素がVue.jsの管理下となり、ViewModelに紐付けられる
* `data`: 上記ViewModelにバインドする値

また、html側でデータバインディングに利用するディレクティブ`v-repeat`や`v-on`を利用、さらにFilterも利用。`{{}}`はデータバインディングに利用する記法で、値が出力される。

* `v-repeat`で、バインディングした配列todosによって子ViewModelを作成
* `v-on`のところで、クリック時に、バインディングした値doneをtrue <=> falseに変更するよう設定
* h1タグの部分で、`uppercase`というFilterを利用。`|`で区切って呼び出すことで、バインディングした値に処理を行う。今回は大文字化。

※理解がまだ浅いので、解釈が誤っている恐れあり


実際に出力されるHTML（の一部）は以下。バインディングした値がリスト表示され、クリックするごとにclassが切り替わる。この挙動を上記のようにシンプルなjsのコードで表現できる。

```html:todo.html
<div id="demo">
  <h1>TODOS</h1>
  <ul>
    <li class="done">
      Learn JavaScript
    </li><li>
      Learn Vue.js
    </li><!--v-repeat-todos-->
  </ul>
</div>
```

## サンプル2 (markdownエディタ)

```coffeescript:markdown.coffee
editor = new Vue
  el: '#editor',
  data: {
    input: '# hello'
  },
  filters: {
    marked: marked
  }
```

```html:markdown.html
<html>
  <head>
    <title>Vue.js sample</title>
    <style>
      html, body, #editor {
        margin: 0;
        height: 100%;
        font-family: 'Helvetica Neue', Arial, sans-serif;
        color: #333;
      }

      textarea, #editor div {
        display: inline-block;
        width: 49%;
        height: 100%;
        vertical-align: top;
        -webkit-box-sizing: border-box;
        -moz-box-sizing: border-box;
        box-sizing: border-box;
        padding: 0 20px;
      }

      textarea {
        border: none;
        border-right: 1px solid #ccc;
        resize: none;
        outline: none;
        background-color: #f6f6f6;
        font-size: 14px;
        font-family: 'Monaco', courier, monospace;
        padding: 20px;
      }

      code {
        color: #f66;
      }
    </style>
  </head>

  <body>
    <div id="editor">
      <textarea v-model="input"></textarea>
      <div v-html="input | marked"></div>
    </div>

    <script src="http://cdnjs.cloudflare.com/ajax/libs/marked/0.3.2/marked.min.js">
    </script>
    <script src="http://cdnjs.cloudflare.com/ajax/libs/vue/0.10.6/vue.min.js"></script>
    <script src="../js/markdown.js"></script>
  </body>
</html>
```

markdownをパースできるように、marked.jsを利用。

インスタンス作成のオプションとして`filters`を利用。先の例でも出てきたようにHTML側で利用する。

HTML側では`v-model`や`v-html`といったディレクティブを使用。

* `v-model`で、textareaタグ内にバインディングした文字列を展開
* `v-html`で、バインディングした文字列をフィルタリング（marked.jsでパース）したHTMLを該当div内に展開

実際に出力されるHTML（の一部）は以下のようになり、バインディングした`# hello`という文字列がmarkdownとして認識され、divタグ内でh1タグ内になっているのが分かる。当然Vue.jsによって双方向にデータバインディングされているため、textareaの中身を編集すれば、隣のdiv内のHTMLも変更される。

```html:markdown.html
<div id="editor">
  <textarea></textarea>
  <div><h1 id="hello">hello</h1>
  </div>
</div>
```

## おわり
まだまだ手に馴染んでいないが、Vue.js、触っててかなり面白い。引き続きべんきょうする