React、Angularになじめなかった僕に手を差し伸べてくれたVue.js

[ギークハウス Advent Calendar 2016](http://www.adventar.org/calendars/1891)の12月２２日の記事です。

他の方とは、全然違う雰囲気になってしまいましたが、読んでいただけると幸いです。

# なぜVue.js？？
普段の仕事では、Ruby/Railsなので、フロントエンド周りは、jQueryにCoffeeScriptで片手間感覚...
　　　　↓
しかし最近のフロントエンド界隈は、良くも悪くも盛り上がっていて楽しそうだなあと思う日々。
　　　　↓
いろいろ、ググって調べてみると、ES6、Babel、Reactふむふむ...🤔
ん？？ Webpack? JSX?? Flux?? Redux??
「落ち着け！とりあえず日本語でOK」状態。。正に[この記事](http://kikuchi1201.hateblo.jp/entry/2016/10/26/172404)で書かれている状態そのものでした。
　　　　↓
Reactとかでイケてるフロントエンド開発をちょっと試したいと思っても、BabelやWebpackの設定など環境構築でつまづき、肝心のアプリケーション開発に着手できず、疲弊していく自分。。
![pose_zetsubou_man.png](https://qiita-image-store.s3.amazonaws.com/0/41385/f2fff6b7-7671-11e8-fcdc-c6f98f87239f.png)

### それでも、諦めたくなかった日々を過ごし....

### Vue.js!! 君と出会ったよ🤗
![figure_ta-da.png](https://qiita-image-store.s3.amazonaws.com/0/41385/b54a1a84-fa68-cb5d-d5bd-5339f8fdd01c.png)


ということで、Vue.jsのご紹介

## Vue.jsとは？
> The Progressive JavaScript Framework
> 「親しみやすい」　「融通がきく」　「高性能」

だそうです。
https://jp.vuejs.org/

ポイントとしては、以下の3点が主な特徴

- シンプル
- リアクティブ
- コンポーネント指向

ひとつずつ見ていきましょう。

## シンプル
### 宣言的にビューを作ることができる
`new Vue`というインスタンスを作り、ビューで指定したIDを元に呼び出すというイメージで割りと違和感なく理解できた。


```index.html
<div id="app">
  {{ message }}
</div>
```

```app.js
var app = new Vue({
  el: '#app',
  data: {
    message: 'Hello Vue!'
  }
})
```

他にも、ビュー上で、条件分岐やループを使うこともできる
`v-if`や`v-for`がVue.jsのAPI

```index.html
<div id="app">
  <ol>
    <li v-for="todo in todos">
      <p v-if="todo.seen">
        {{ todo.text }}
      </p>
    </li>
  </ol>
</div>
```

```app.js
var app = new Vue({
  el: '#app',
  data: {
    todos: [
      { text: 'Learn JavaScript', seen: true },
      { text: 'Learn Vue', seen: true },
      { text: 'Build something awesome', seen: false }
    ]
  }
})
```

ビューに表示されるのは、`seen`がtrueである`Learn JavaScript`と`Learn Vue`

他にも便利な機能がたくさん。詳細は、[Vue.js公式APIドキュメント](https://jp.vuejs.org/v2/api/)

## リアクティブ
そもそもの「リアクティブ」自体の定義が、難しいが、Vue.jsにおける「リアクティブ」は、JavaScript上で、定義したデータが何か変更されたと同時にビューも同期的(synchronize)に変更されるシステムだと解釈する（もっと抽象化したよい表現があるかもしれない）。

自分もまだまだよくわかっていないが、Vue.jsのおけるリアクティブシステムは以下の図で表すことができ、データの状態を監視するWatcher(ウォッチャー)というのが、欠かせない存在らしい。
ここらへんはまだよくわかってないですね、すみません🙇

![image](https://qiita-image-store.s3.amazonaws.com/0/41385/876cb6b5-1660-b6f5-173b-2082ec61c621.png)

>[Vuex公式ドキュメント](https://vuex.vuejs.org/ja/intro.html)より引用

## コンポーネント指向
DOMを構造的に管理するためのアーキテクチャ
イメージとしては、以下の図がわかりやすい。

![image](https://qiita-image-store.s3.amazonaws.com/0/41385/baef3557-4e01-e83a-d2a6-2b8ffd0ba608.png)

>[Vue.js公式ドキュメント「コンポーネントによる構成」](https://jp.vuejs.org/v2/guide/#コンポーネントによる構成)より引用

ツリー構造としてDOMをコンポーネントという１つの単位で、作成し他のコンポーネントと組み合わせることで、

- 疎結合
- 再利用可能
- データの状態管理を整理できる

といった恩恵が受けられる。

実装例として、ちょっとしたTodoアプリで、Todo一覧を表示しようとする例で考察してみる。
まず、`todo-item`というコンポーネントを作成する。
このコンポーネントの役割としては、`todo`というオブジェクトが持つ`text`をリスト形式で表示することである。

次に`groceryList`のような実際のデータを持つVueインスタンスを作成する。

```todo_component.js
Vue.component('todo-item', {
  props: ['todo'],
  template: '<li>{{ todo.text }}</li>'
})

var app = new Vue({
  el: '#app',
  data: {
    groceryList: [
      { text: 'Vegetables' },
      { text: 'Cheese' },
      { text: 'Whatever else humans are supposed to eat' }
    ]
  }
})
```

上記で作成したVueインスタンス変数を`<div id="app">`のDOMとして指定する。
これで、このDOMにおいてVueインスタンス変数である`app`のデータを呼び出すことができる。
そして、作成したコンポーネントをこのDOMに注入し、`v-bind`でVueインスタンスで定義した`todo`をループして呼び出すことができる。

しかし、実際に、ビューにデータを受け渡しているのはコンポーネント内にある`props`である。
これは、親となるスコープ(この場合は、Vueインスタンス変数`app`)から子コンポーネントである`todo-item`へとデータを渡す。

```index.html
<div id="app">
  <ol>
    <!-- todo オブジェクトによって各 todo-item を提供します。それは、内容を動的にできるように表します。-->
    <todo-item v-for="item in groceryList" v-bind:todo="item"></todo-item>
  </ol>
</div>
```

この例から言えることは、

- アプリケーションを小さな単位で分割できる
- 疎結合な子コンポーネントの作成ができる
- 親コンポーネントに影響を与えず改良できる

つまり、jQueryでは難しかったDOMの構造化をフレームワークとして担保することができると言える。

結果

```
1. Vegetables
2. Cheese
3. Whatever else humans are supposed to eat
```

他にも、フォーム入力のバリデーション機能やアニメーション機能もあるが、ここでは、Vue.jsがどのような思想で、UIを構築していくかについて、触れたかったため、詳細は、[素晴らしい公式ドキュメント](https://jp.vuejs.org/v2/api/)で:+1:

## 動くものを作ってみる
月並みですが、題材は、Todoアプリにしました。
対象のフレームワークがどのようにデータの入力・出力を行うかを知れば、基本的なフレームワークの理解は進むと思ったためです。

ただ、せっかくなので、なるべくモダンで普段使いを意識した構成で開発してみようと思います。

## 構成

- Fluxアーキテクチャに基づいたアプリケーション作成
- 実装は、ES6
- Electronでデスクトップアプリ化

## Fluxアーキテクチャに基づいたアプリケーション作成
[DOMをMVCで管理するのは辛いよね](http://postd.cc/is-mvc-dead-for-the-frontend/)というところから、提唱されたアーキテクチャー
Fluxの概要については、[漫画で説明するFlux](https://medium.com/@sotayamashita/%E6%BC%AB%E7%94%BB%E3%81%A7%E8%AA%AC%E6%98%8E%E3%81%99%E3%82%8B-flux-1a219e50232b#.kkglld8lb)がわかりやすかったので、これを読むと良いかも:metal:

簡単に言うと、

- MVCだと、規模が大きくなり、どのモデルがどのイベントによっていつ何にデータが変更されたのかが把握することが難しい
- Fluxによって、データの変更を受け付けつけるもの、変更を監視するもの、実際に変更を行うものなどのように責務を分けるようにした

そして、このFluxアーキテクチャーに基づいたライブラリが各JSフレームワーク毎に用意されている。
Reactなら「Redux」
Vue.jsなら **「Vuex」** である。

## Vuexとは？
Vue.jsにおける状態管理パターンを実現するためのアーキテクチャであり、ライブラリ。
アーキテクチャの思想としては、、[Flux](https://facebook.github.io/flux/docs/overview.html)、 [Redux](http://redux.js.org/) そして [The Elm Architecture](https://guide.elm-lang.org/architecture/)から影響を受けている。

### 状態管理パターンとは？
まず、状態管理パターンの必要性として、比較的ビュー部分というのは変更が激しい部分という前提がある。
そのため、ビューの表示を構成するDOMオブジェクトの状態が、いつどのように変更したのかを把握するためにこのパターンが、一つの解法アプローチとなっている。

具体的には、以下の３つの概念が状態管理パターンの中心となる。

- 状態(state)
	- そのアプリがどういうAPIあるいはデータ属性を持っているかを情報源として集約させた場所
- ビュー
	- 実際にユーザーに表示される画面。そこから、ユーザーからの入力など何らかのリクエストを受け付ける場所
- アクション
	- ビューからのユーザー入力に反応して、状態の変更を行う。

これら３つの概念によるデータの流れは、 **単方向データフロー** と呼ばれる。
![Kobito.3OWVgi.png](https://qiita-image-store.s3.amazonaws.com/0/41385/641160ff-ff94-a04d-38fa-3b9d33b937e8.png "Kobito.3OWVgi.png")

>[Vuex公式ドキュメント「Vuexとは何か？」](https://vuex.vuejs.org/ja/intro.html)より引用

これで、単方向データフローにもとづいて、ビューをコンポーネントとして分ければ、管理しやすくなりそうだ。これだけ見ると、非常にシンプルと言える。

しかし、複数の異なるビューやアクションによって、共通の状態を変更したり、共有しようとすると、状態管理はたちまち大変になり辛みが増してくる。

このような課題を解決するため、Vuexでは、共有されている状態を、一箇所に集約し、それを[グローバルシングルトン](https://ja.wikipedia.org/wiki/Singleton_%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3)で管理するという方法が取られている。

この方法により、コンポーネントツリーが大きなビューとなり、どのコンポーネントからでも状態へのアクセスやアクションをトリガーできる。
![Kobito.dVhfmv.png](https://qiita-image-store.s3.amazonaws.com/0/41385/ed3328c0-d807-c38e-1946-80cb337b606d.png "Kobito.dVhfmv.png")

>[Vuex公式ドキュメント](https://vuex.vuejs.org/ja/intro.html)より引用

さらなる詳細は、[公式ドキュメント](https://vuex.vuejs.org/ja/)がよく整理されていて良い！！しかも日本語訳されているのはありがたい:smile:

## 実装

### 環境構築
ES６で実装したいので、トランスパイラーとして、Babelを使用するが、バンドルツールとして、Webpackも必要になってくる。
しかし、自分は、Webpackの使い方があまりわかっておらず、あまりこういうところで時間もかけたくない。

そんなニーズを満たせるのが、 **vue-cli** である。
https://github.com/vuejs/vue-cli

これは、ドキュメントにある指示通りにコマンドを叩けば、BabelやWebpackの設定を自動で構築してくれる優れものである。

ただ、さらに欲張りな自分は、Electron上で、このような環境の自動構築もできたら最高だよなあと思っていた矢先に！！

なんと、 **[electron-vue](https://github.com/SimulatedGREG/electron-vue)**なるものもある😀

今回は、これで環境構築をしてみる。

```
# Install vue-cli and scaffold boilerplate
npm install -g vue-cli
vue init simulatedgreg/electron-vue my-project

# Install dependencies and run your app
cd my-project
npm install
npm run dev
```

`vue init`のコマンドを実行するとこんな感じで、いろいろとオプションの設定を聞かれるので、お好みで！
今回は、vuexなどを使うので、ひたすらEnter
<img width="484" alt="スクリーンショット 2016-12-30 17.31.05.png" src="https://qiita-image-store.s3.amazonaws.com/0/41385/33abbcea-bb38-1880-22ca-7ffbd961c360.png">

ファイル構成は、このように自動生成され、基本的には、`app`以下のファイルを実装していく。
<img width="144" alt="スクリーンショット 2016-12-30 17.34.55.png" src="https://qiita-image-store.s3.amazonaws.com/0/41385/031fd904-c369-9455-d094-40611f7f0201.png">

`index.ejs`が最終的にページを描画するHTMLだが、特に編集する必要はない

```index.ejs
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title><%= htmlWebpackPlugin.options.title %></title>
  </head>
  <body>
    <div id="app"></div>
    <!-- webpack builds are automatically injected -->
  </body>
</html>
```

`main.js`で、各種モジュールをインポートし、Vueインスタンスを作成する。

```main.js
import Vue from 'vue'
import Electron from 'vue-electron'
import Router from 'vue-router'

import App from './App'
import routes from './routes'

Vue.use(Electron)
Vue.use(Router)
Vue.config.debug = true

const router = new Router({
  scrollBehavior: () => ({ y: 0 }),
  routes
})

/* eslint-disable no-new */
new Vue({
  router,
  ...App
}).$mount('#app')
```

実際にビューを作っていくのは`.vue`という拡張子がついたファイルで実装していく
まずは、ベースとなる`LandingPageView.vue`からいじっていく

```LandingPageView.vue
<template>
  <div>
    <taskList></taskList>
  </div>
</template>

<script>
import TaskList from './LandingPageView/TaskList'

export default {
  components: {
    TaskList
  },
  name: 'landing-page'
}
</script>

<style lang=scss>

</style>
```

個人的には、この`vue`というファイルに、それぞれhtml部分の`template`、js部分の`script`、CSS部分の`style`という構成がすごく気に入っている。

`<taskList></taskList>`というタグは、親コンポーネントである`LandingPaageView.vue`の子コンポーネントを呼び出している部分である。


### 次にVuexによる状態管理を実装していく
まずは、アプリケーションの状態を保持するコンテナである`Store`を作っていく。
このStoreで宣言されたミューテーション(`mutations`)を介して、状態の変更が、リアクティブに更新される。

```store.js
import Vue from 'vue'
import Vuex from 'vuex'
import state from './state'
import mutations from './mutations'
import * as actions from './actions'
import plugins from './plugins'

Vue.use(Vuex)

export default new Vuex.Store({
  state,
  mutations,
  actions,
  plugins,
  strict: true
})
```

次に、`mutationns`でどのような変更を行うかを宣言的に記述していく。
しかし、オプションであるが、`mutation-type.js`というファイルを介しての`mutationns`の実装を行うとさらに良い。

理由として、自分以外の誰かが、アプリケーションの実装を読んだ際、`mutation-type.js`さえ読めば、そのアプリケーションがどんな動作をするのかを理解することが簡単になるからである。

今回で言えば、Todoアプリケーションであるため、以下のようなミューテーションとなる。

```mutation-type.js
export const ADD_TASK = 'ADD_TASK'
export const TOGGLE_TASK = 'TOGGLE_TASK'
export const DELETE_TASK = 'DELETE_TASK'
export const EDIT_TASK = 'EDIT_TASK'
export const TOGGLE_ALL_TASK = 'TOGGLE_ALL_TASK'
export const CLEAR_COMPLETED = 'CLEAR_COMPLETED'
```

そして、実際にミューテーションを実装していく。

```mutations.js
import { ADD_TASK, TOGGLE_TASK, DELETE_TASK, EDIT_TASK, TOGGLE_ALL_TASK, CLEAR_COMPLETED } from './mutation-types'

export default {
  [ADD_TASK] (state, { text }) {
    state.tasks.push({
      text,
      done: false
    })
  },
  [TOGGLE_TASK] (state, { task }) {
    task.done = !task.done
  },
  [DELETE_TASK] (state, { task }) {
    state.tasks.splice(state.tasks.indexOf(task), 1)
  },
  [EDIT_TASK] (state, { task, value }) {
    task.text = value
  },
  [TOGGLE_ALL_TASK] (state, { done }) {
    state.tasks.forEach((task) => {
      task.done = done
    })
  },
  [CLEAR_COMPLETED] (state) {
    state.tasks = state.tasks.filter(task => !task.done)
  }
}
```

Vuexにおいて、状態の変更を行う際には、ミューテーションをコミットすることで初めて状態の変更が可能となる。具体的なコミットの実装は以下のとおりである。

```actions.js
import { ADD_TASK, TOGGLE_TASK, DELETE_TASK, EDIT_TASK, TOGGLE_ALL_TASK, CLEAR_COMPLETED } from './mutation-types.js'

export const addTask = ({ commit }, text) => {
  if (text) {
    commit(ADD_TASK, {
      text
    })
  }
}

export const toggleTask = ({ commit }, task) => {
  commit(TOGGLE_TASK, {
    task
  })
}

export const deleteTask = ({ commit }, task) => {
  commit(DELETE_TASK, {
    task
  })
}

export const editTask = ({ commit }, task) => {
  commit(EDIT_TASK, {
    task
  })
}

export const toggleAllTask = ({ commit }, task) => {
  commit(TOGGLE_ALL_TASK, {
    task
  })
}

export const clearCompleted = ({ commit }, task) => {
  commit(CLEAR_COMPLETED, {
    task
  })
}
```

ここまでをおさらいすると、
1. 状態管理に必要な各種モジュールをインポートしたStoreを作成
2. 状態を変更するためのミューテーションを宣言的に定義
3. 実際に状態を変更するためにミューテーションをコミットする

### そして最後に実際のTodoアプリケーションの実装を進めていく。
全ソースはこちら
https://github.com/samuraikun/electron-vue-todo

#### 今回のTodoアプリの要件としては、大雑把にこんな感じ
* 基本的なTodoの追加、編集、削除
* Todoが終わったかどうかはチェックのON/OFF
* 終わったTodoとまだ終わっていないTodoをフィルタリングして表示/非表示を切り替え

#### まずは、Todoの一覧リストそのものを実装していく。
全ての実装を載せると、記事が長くなりすぎるので（~~すでに長いが~~）、一部抜粋していくと、

まずテンプレート部分

```TaskList.vue
<template>
...
...

<!-- main section-->
    <div v-show="tasks.length" class="main">
      <input type="checkbox" :checked="allChecked" @change="TOGGLE_ALL_TASK({ done: !allChecked })" class="toggle-all"/>
      <ul class="task-list">
        <task v-for="task in filteredTasks" :task="task"></task>
      </ul>
      <!-- footer -->
      <footer v-show="tasks.length" class="footer">
        <span class="task-count">
          <strong>{{ remaining }}</strong>
          {{ remaining | pluralize('item') }} left
        </span>
        <ul class="filters">
          <li v-for="(val, key) in filters">
            <a :href="'#/' + key" :class="{ selected: visibility === key }" @click="visibility = key">{{ key | capitalize }}</a>
          </li>
        </ul>
        <button v-show="tasks.length &gt; remaining" @click="CLEAR_COMPLETED" class="clear-completed">Clear completed</button>
      </footer>
    </div>
...
...
</template>
```
`v-for`で複数あるTodoを表示したり、Todoをフィルタリングするためのメソッドを呼び出している。

フィルタリングのロジックは、`<script></sript>`タグ内で実装していく。

```TaskList.vue
<script>
  import { mapMutations } from 'vuex'
  import Task from './Task'

  const filters = {
    all: tasks => tasks,
    active: tasks => tasks.filter(task => !task.done),
    completed: tasks => tasks.filter(task => task.done)
  }

  export default {
    name: 'TaskList',
    components: { Task },
    data () {
      return {
        visibility: 'all',
        filters: filters
      }
    },
    computed: {
      tasks () {
        return this.$store.state.tasks
      },
      allChecked () {
        return this.tasks.every(task => task.done)
      },
      filteredTasks () {
        return filters[this.visibility](this.tasks)
      },
      remaining () {
        return this.tasks.filter(task => !task.done).length
      }
    },
    methods: {
      addTask (e) {
        var text = e.target.value
        if (text.trim()) {
          this.$store.commit('ADD_TASK', { text })
        }
        e.target.value = ''
      },
      ...mapMutations([
        'TOGGLE_ALL_TASK',
        'CLEAR_COMPLETED'
      ])
    },
    filters: {
      pluralize: (n, w) => n === 1 ? w : (w + 's'),
      capitalize: s => s.charAt(0).toUpperCase() + s.slice(1)
    }
  }
</script>
```

実装を読むと、なんとなくその意味がわかる。特に特徴的なのは、`computed`だが、これは`算出型プロパティ`と呼ばれるVue.jsの機能の１つである。
一見すると、`methods`と同じように何かしらの処理を実装しており、その違いは何だろうか？という疑問が出るかもしれない。

違いとしては、
>算出プロパティは依存関係にもとづきキャッシュされる

という点である。
つまり、再度、`computed`で定義された処理を即座に呼び出すことができる。
これは、巨大な配列をループしたり多くの計算を必要とする際に、便利な機能と言える。

Todo一覧のコンポーネントができたので、その子コンポーネントである`Task.vue`を実装していく。

テンプレート部分でTodoのフォームを作成

```Task.vue
<template>
  <li :class="{ completed: task.done, editing: editing }" class="task">
    <div class="view">
      <input type="checkbox" :checked="task.done" @change="TOGGLE_TASK({ task: task })" class="toggle"/>
      <label v-text="task.text" @dblclick="editing = true"></label>
      <button @click="DELETE_TASK({ task: task })" class="destroy"></button>
    </div>
    <input v-show="editing" v-focus="editing" :value="task.text" @keyup.enter="doneEdit" @keyup.esc="cancelEdit" @blur="doneEdit" class="edit"/>
  </li>
</template>
```

Todoの編集、削除機能のロジックを実装していく
ちなみに`mapMutations`は、コンポーネント内でミューテーションをコミットできるようにする[ヘルパー](https://vuex.vuejs.org/ja/mutations.html)である。

```Task.vue
<script>
import { mapMutations } from 'vuex'

export default {
  name: 'Task',
  props: ['task'],
  data () {
    return {
      editing: false
    }
  },
  directives: {
    focus (el, { value }, { context }) {
      if (value) {
        context.$nextTick(() => {
          el.focus()
        })
      }
    }
  },
  methods: {
    ...mapMutations([
      'EDIT_TASK',
      'TOGGLE_TASK',
      'DELETE_TASK'
    ]),
    doneEdit (e) {
      const value = e.target.value.trim()
      const { task } = this
      if (value) {
        this.DELETE_TASK({
          task
        })
      } else if (this.editing) {
        this.EDIT_TASK({
          task,
          value
        })
        this.editing = false
      }
    },
    cancelEdit (e) {
      e.target.value = this.task.text
      this.editing = false
    }
  }
}
</script>
```

### これにて完成！！

<img width="954" alt="スクリーンショット 2016-12-30 22.57.11.png" src="https://qiita-image-store.s3.amazonaws.com/0/41385/c1b7f4d1-2753-8411-e894-261f80f47d07.png">

## 感想
* 前評判通り、学習コストはフルスタックなAnguar、JSXを強いられるReactと違いシンプル
* 公式のドキュメントが日本語訳もされており、わかりやすい
* Reactのようにコンポーネントによる開発、Anguar1.xのように、ディレクティブやバインディングができるなど、他のフレームワークの良いところを上手く導入できている印象

## 今後
PHPフレームワークのLaravelがVue.jsを導入したり、[私たちはなぜReactではなくVue.jsを選んだのか](http://postd.cc/why-we-chose-vuejs-over-react/)の記事で言われているように、React、Angularで違和感を感じた部分をVue.jsが補っている部分があるから人気が出ているかなあと感じた。

もしもだけれど、RailsもVue.jsを公式に導入してくれたら嬉しいなあ😆