Vue.js（v2.x.）を使ってメモアプリを作ってみた

この記事は、[Vue.js Advent Calender 2016](http://qiita.com/advent-calendar/2016/vue) 5日目の記事です。

Vue.jsを使って、メモアプリを作ってみました。

- [Github](https://github.com/akifo/vue-memo) 
- [Demo](https://vue-memo.firebaseapp.com/) 

```
* この記事では、説明のためのサンプルコードを書いていますが、Githubにあるコードとは似て異なります。ご注意ください。
```
![screen-shot.jpg](https://qiita-image-store.s3.amazonaws.com/0/62259/521dbb26-b088-86a5-cfb6-4b352a460878.jpeg)

## 環境
vue-router、vuex、Firebase を使っています。
vuefire はあえて使用していません。
vuex から Firebase を呼び出しています。


- [vue-router@2.x.](https://github.com/vuejs/vue-router) 
- [vuex@2.x.](https://github.com/vuejs/vuex) 
- [vuex-router-sync@3.0.0](https://github.com/vuejs/vuex-router-sync)
- babel (es2015, stage-2)
- webpack
- webpack-dev-server
- [firebase@3.6.2](https://www.npmjs.com/package/firebase)
- [lodash@4.17.x](https://lodash.com/docs/4.17.2)
- [moment@2.17.x](http://momentjs.com/)


## 画面遷移
vue-router を使い、３つの画面を管理しています。
- Index（タイムライン）
- Viewer（ひとつのメモを見る）
- Editor（メモを追加、編集）
それぞれ、URLが個別に割り当てられるのがポイントです。

```js
var router = new VueRouter({
  routes: [
    {
      path: '/',
      name: 'index',
      component: Index
    }, {
      path: '/editor',
      name: 'newEditor',
      component: Editor
    }, {
      path: '/editor/:id',
      name: 'updateEditor',
      component: Editor
    }, {
      path: '/viewer/:id',
      name: 'viewer',
      component: Viewer
    }
  ]
})
```

## 状態管理
vuex を使い、全コンポーネントから利用できるデータを管理しています。
``Backend API`` にあたる部分が ``Firebase`` になっています。

![vuex.png](https://qiita-image-store.s3.amazonaws.com/0/62259/380e40c8-3189-bd34-dc3d-7098f39aed6d.png)

## Vue Component から vuex の state を取得

```js:mapGettersを使ってStateを取得する例
computed: mapGetters(['user', 'currentUserName'])
```

vuex で保持する state を Component 内で直接変更することはできませんので、取得したデータを Component 内で変更するとエラーになります。

```js:ダメな例
<input v-model="currentUserName">
```

その代わりに、次のように、書く事ができます。

```js:良い例
<input :value="currentUserName" @input="something">
```

## Vue Component から Vuex の Action を呼ぶ

```js:他にmethodがない場合の例
methods: mapActions(['fetchMemos', 'signIn', 'signOut'])
```

``signIn`` は ``template`` 内で ``<p click="signIn">signIn</p>`` のように呼び出せます。

```js:他にmethodがある場合&Component内のmethodからactionをdispatchする例
methods: {
  ...mapActions(['fetchMemos']),
  addCount () {
    this.count++
  },
  submit () {
    this.$store.dispatch('setUserInfo') 
  }
}
```

``mapActions`` で Vuex に書いた Actions を取得できます。

## Vuex 

- Action を呼び出すのが Dispatch
- Mutation を呼び出すのが Commit
- Mutation では同期処理のみ
- Component から Vuex の State を 呼び出すのが Getter
- Firebase は Backend API として Actions から呼び出すのを基本にした
- Firebase のログイン状態監視の部分だけ、 Backend API が (Vue, Vuex) に依存する

## Firebase

- rule を決めるだけで良い。便利
- vuex とのやりとりは、全て Promise にした
- サーバーサイドのコード１つも書かずにできた

```js:database-rules.jsonの一部
{
  "rules": {
    ".read": true,
    "users": {
      "$uid": {
        ".write": "auth !== null && auth.uid === $uid",
        "name": {
          ".validate": "newData.isString() && 2 <= newData.val().length && newData.val().length <= 100"
        }
      }
    },
    "memos": {
      "$uid": {
        ".write": "auth !== null && auth.uid === $uid",
        "$mid": {
          "title": {
            ".validate": "newData.isString() && newData.val().length <= 200"
          }
        }
      }
    },
    "publicMemos": {
      "$mid": {
        ".write": "root.child('memos/' + auth.uid ).hasChild($mid)",
        "title": {
          ".validate": "newData.isString() && newData.val().length <= 200"
        },
        "author": {
          "uid": {
            ".validate": "newData.val() == auth.uid"
          },
          "name": {
            ".validate": "newData.isString() && 2 <= newData.val().length && newData.val().length <= 100"
          }
        }
      }
    }
  }
}
```

大きく分けて、３つのツリーがあります。

- users(ユーザーの名前を管理、ログイン管理は、Firebaseが自動でやってくれます)
- memos（ユーザー個別ID別にメモを管理）
- publicMemos（全てのメモを一元管理）

memos と publicMemos には同じデータが入っています。
memos には個別ユーザーID毎にツリーがあり、その下に各ユーザーのメモを格納します。
publicMemos には、全てのメモがズラーっと入っています。
memos に存在するメモしか、publicMemosには書き込みができないよう権限の設定をしました。
そのため、書き込み・削除の順番を意識する必要があります。
全てのツリーはログインしていないと書き込みができません。

## Firebase と vuex の共存について

[VueFire - Firebase meets Vue.js](https://firebase.googleblog.com/2016/04/vuefire-firebase-meets-vuejs_0.html)

Evan氏が2016年4月13日に書いている Firebaseブログです。
Vuex と Firebase の 統合について Vue.jsチームは調査中とあります。
Githubで、vuefire、vuexfire、vuex、Firebase 関連のサンプルを探したものの、「これは良さそう」というレポジトリはありませんでした。そのため、今回 vuefire はあえて使用せずに、vuex の Backend API の 部分に Firebase を配置しました。

vuefire の良いとこどりをした構造を考えて、今回のようなアプリに組み込めたら良いんですが、いずれにせよ大規模になると複雑になりそうです。vuefire と vuex の２つで状態管理をしたら良いのかな。。


## 参考

今回、この記事を書くにあたり、いくつかのレポジトリや記事を参考にさせていただきましたので、ご紹介させて頂きます！感謝！


### [Tech Blog Vue.js + Vuexで開発をしてみよう！](http://engineer.recruit-lifestyle.co.jp/techblog/2016-09-15-try-vue-flux/) 
vuexを理解するのに参考になりました。

### [【意訳】Webpackの混乱ポイント](http://qiita.com/chuck0523/items/caacbf4137642cb175ec)
webpackの理解が深まりました。

### [Firebase Web Codelab](https://codelabs.developers.google.com/codelabs/firebase-web/#0)
Friendly Chat というアプリ制作を通して、 Firebase の使い方を学ぶ事ができる公式サービスです。
60分で、Firebase を Web で使うための基礎を学ぶ事ができます。

### [vue-cli](https://github.com/vuejs/vue-cli)
ここにある Webpack のテンプレートをベースに、今回作成した **Vue-memo** の構造を考えました。
**Vue-memo** は、**vue-cli** にあるテンプレート **webpack** と **webpack-simple** の中間くらいの規模です。

### [vue-boilerplate](https://github.com/funkyLover/vue-boilerplate)
vuex と vue-router の共存させる書き方の参考になりました。

### [vuex](https://github.com/vuejs/vuex)
vuex のレポジトリにあるサンプルコードを何回も読みました。
できる限りここのサンプルから構造を真似して、自分でロジックを勝手に作らないように意識しました。

### [vue-hackernews-2.0](https://github.com/vuejs/vue-hackernews-2.0)
Vue.js 関連のサンプルコードの中で、最も参考にするべきレポジトリではないでしょうか。
しかし、私にはサーバーサイドレンダリングなど、理解が追いついておらず。。
いつかはサンプルコードを全て理解したい。そんなレポジトリです。

## 作ってみて感じた事

- Webpack （もしくは、 Browserify）を使いこなすのが難しい
- babel の理解が必要(ES2015, stage-2など）
- webpack-dev-server の hot-reload は開発が捗る
- Vue.js で、作ってみた系の記事・レポジトリが少ない（見つけられないだけかも。。）
- ベストプラクティスを探すより、プロジェクトに合うアーキテクチャをチームで考えるのが良さそう

## babel の presets で stage-2 を入れないと、spread演算子``...``を使えない。

babel の presets を設定します。

```js:webpack.config.js
  babel: {
    presets: ['es2015', 'stage-2'],
  }
```

これで、次のような書き方が可能になります。

```js:VueComponent内のコード例
...mapGetters(['currentMemo', 'currentMemoID'])
```

このコード、Github にある Vue.js 関連の サンプルによく出てきます。


## ちなみに、

今回制作した **Vue-memo** は１年前（2015/12/13）に書いた記事[「Vue.jsを使って、簡易メモアプリを作る。facebookログイン機能付き。」](http://qiita.com/akifo/items/114398e718b43245f241)を **Vue.js v2.x.** に対応させるという目標のもと、結局全て作り変えたたものです。


## 最後に

プルリクエスト大歓迎です。

日本語Vue.jsメンバーの皆様にはこの場をお借りして、いつも素晴らしい情報を発信してくださっているお礼を申し上げます。本当にありがとうございます。


明日は@mediba-kanamoriさんです。