TodoAppでVue.js入門(1)

<img src="https://jp.vuejs.org/images/logo.png" height=200>

# やりたいこと

Reactに疲れて去年から最近流行りのVue.jsに手を出してみた。最終的には以下の構成でWebアプリを作りたい。

- スクリプト: Typescript
- マークアップ: pug
- スタイル: stylus
- UIフレームワーク: Element (http://element.eleme.io/)
- ルーティング: vue-router (https://router.vuejs.org/)
- 状態管理: vuex (https://vuex.vuejs.org/)
- ビルド: Webpack
- エディタ: VSCode

今回はまずは

- スクリプト: ES6
- マークアップ: html
- スタイル: css3
- ルーティング: vue-router (https://router.vuejs.org/)
- ビルド: Webpack
- エディタ: VSCode

でトップページとシンプルなTodo管理画面の２つを作ってみる。
成果物 -> https://github.com/lucidfrontier45/vue-todo-test/tree/0.1.1

# 準備

## 環境

- Ubuntu 16.04
- node v8.1.2

## インストール

[vue-cli](https://github.com/vuejs/vue-cli)を使用するとcreate-react-app並に簡単に始められる。

```zsh
$ npm i -g yarn veu-cli
$ vue-init webpack vue-todo
```
vue-initはいろいろ聞いてくるが基本的には全部yesで答えておけばOK。これでプロジェクトが作成されるので依存ライブラリをインストールし、webpackのdev-serverも簡単に立ち上げられる。


```zsh
$ cd vue-todo
$ yarn install
$ yarn run dev
```

ちなみに自分は勝手にブラウザを立ち上げられるのが嫌なので`config/index.js`の`dev.autoOpenBrowser`を`false`にしている。

# TodoApp実装

プロジェクトはcreate-react-appとほぼ同じで`index.html`のroot divにアプリがすべてレンダリングされる仕組みだ。
Vueの特徴としてはいろいろなスタイルの書き方があり、中でもSingle File Compinent(SFC)という`.vue`ファイルにhtml/css/jsすべてを書けるというのが一番おもしろい。HTMLもstyle,scriptでcssやjsをかけるが、それをパーツごとに分解できるというイメージだ。

以下のファイルがTodoAppの本体部分。

```html:Todos.vue
<template>
  <div>
    <div>
      <input type="text"
             v-model="input"></input>
      <button @click="handleAdd">Add</button>
      <select v-model="mode">
        <option label="All"
                value="all"></option>
        <option label="Completed"
                value="completed"></option>
        <option label="Incomplete"
                value="incomplete"></option>

      </select>
    </div>
    <ul>
      <li v-for="todo in filteredTodos"
          :key="todo.id">
        <button @click="() => handleToggle(todo.id)">
          <span v-if="todo.completed">
            restore
          </span>
          <span v-else>
            done
          </span>
        </button>
        {{todo.msg}}

      </li>
    </ul>
  </div>
</template>

<script>
import { addTodo, todos, toggleTodo } from "../todos"

function filterTodos(todos, mode) {
  if (mode === "all") {
    return todos
  } else if (mode === "completed") {
    return todos.filter(todo => todo.completed)
  } else {
    return todos.filter(todo => !todo.completed)
  }
}

export default {
  data: function() {
    return {
      input: "",
      mode: "incomplete",
      todos
    }
  },
  methods: {
    handleAdd: function() {
      const v = this.input.trim()
      if (v.length > 0) {
        addTodo(v)
        this.input = ""
      }
    },
    handleToggle: function(idx) {
      toggleTodo(idx)
    }
  },
  computed: {
    filteredTodos: function() {
      return filterTodos(this.todos, this.mode)
    }
  }
}
</script>

<style scoped>
ul {
  margin-left: 0;
  padding-left: 0;
  transform: translateX(50%);
  text-align: left;
  list-style-position: inside;
}
</style>
```

`<template>`タグでhtml、`<script>`タグでjs、 `<style>`タグ中にcssを書ける。

## 要点

- jsではdata, methods, computedを含んだオブジェクトを作る。dataはreactでいうstateに相当し、methodsは`@click`などで使用するものをここに書く。computedはdataに対する何らかの変換を施し、最終的に表示する変数の内容を作る。computedはdataが変わると自動的に再計算される。
- htmlのテキスト部分には`{{varname}}`、divなどの中の属性の時は`v-bind:attr=varname`か`:attr=varname`のようにして変数を使用できる。ここでの表現にはdata, methods, computedなどで定義したものが使用できる。
- template中の`@click`は`v-on:click`の略で、ボタンなどをクリックした時の挙動を設定できる。指定は関数やアロー演算子を使用した書き方などがある。
- `v-model="input"`は双方向バインドと呼ばれるもので、画面の表示とcomponentのdataの中身を同期できる仕組みである。
- `v-for`, `v-if`などのテンプレートエンジンでお馴染みの機能もある。
- 詳しくは https://jp.vuejs.org/v2/guide/ を参照のこと

# 小並感

- Reactを知っている人にとっては公式ガイドを一度読んでおけば基本的にはそんなに困ることはなさそう。
- それでいてJSXを強要されないのでマークアップ、スタイリングはデザイナーにお願いし、エンジニアはscriptの機能、動作部分に集中できそう。
- vue-cliのおかげで導入の敷居がものすごく低い。

次回はpug,stylusそしてElementを導入する。