Vue.js+Vuex+VuexFireでTodoList

最近、Firebaseを勉強中です。Vuexも勉強中です。
その2つをつなぐための[VuexFire](https://github.com/posva/vuexfire)というライブラリを組み込んで、TodoListを作ってみました。

- [作ったやつ](https://github.com/frost-star/vuextodo/tree/1.0.0)

とりあえず現状だと接続先のFirebaseデータベース残してるから動くけど、その内消すかも。

# コード比較
VuexFireの有無でのStoreのコードを比較してみます。

```js:VuexFireなし
import Vuex from 'vuex';
import Vue from 'vue';
import { ADD_TODO, REMOVE_TODO } from './action-types';

Vue.use(Vuex);

export default new Vuex.Store({
  strict: process.env.NODE_ENV !== 'production',
  state: {
    todos: [],
  },
  mutations: {
    [ADD_TODO](state, text) {
      const todo = {
        id: 0,
        text,
      };
      if (this.state.todos.length !== 0) {
        todo.id = this.state.todos[this.state.todos.length - 1].id + 1;
      }
      this.state.todos.push(todo);
    },
    [REMOVE_TODO](state, id) {
      this.state.todos = this.state.todos.filter(todo => id !== todo.id);
    },
  },
  actions: {
    [ADD_TODO](context, text) {
      context.commit(ADD_TODO, text);
    },
    [REMOVE_TODO](context, id) {
      context.commit(REMOVE_TODO, id);
    },
  },
  getters: {
    getTodos: state => state.todos,
  },
});
```


```js:VuexFireあり
import Vuex from 'vuex';
import Vue from 'vue';
import * as firebase from 'firebase';
import { firebaseMutations, firebaseAction } from 'vuexfire';
import { ADD_TODO, REMOVE_TODO } from './action-types';

const config = {
  databaseURL: 'https://vue-fire-test-3c227.firebaseio.com/',
};
const firebaseApp = firebase.initializeApp(config);
const db = firebaseApp.database();
const todosRef = db.ref('todo');

Vue.use(Vuex);

const INIT_TODO = 'INIT_TODO';
const myPlugin = store => store.dispatch(INIT_TODO, 'test');

export default new Vuex.Store({
  strict: process.env.NODE_ENV !== 'production',
  state: {
    todos: [],
  },
  mutations: {
    ...firebaseMutations,
  },
  actions: {
    [INIT_TODO]: firebaseAction(({ bindFirebaseRef }) => {
      bindFirebaseRef('todos', todosRef, { wait: true });
    }),
    [ADD_TODO]: firebaseAction((context, text) => {
      todosRef.push(text);
    }),
    [REMOVE_TODO]: firebaseAction((context, key) => {
      todosRef.child(key).remove();
    }),
  },
  getters: {
    getTodos: state => state.todos,
  },
  plugins: [myPlugin],
});
```
## Mutation
一番大きな違いはmutationsの処理がほぼ消えたことです。
そもそもTODOのデータ構造でFirebase側でつけられたキーを使うことができるため、idを管理する必要がなくなったというのもあります。
しかし、本当の理由はstateへの変更はActionで呼び出す`bindFirebaseRef`によるFirebaseとの連携処理に限定されるからです。
その処理は非同期になるため、すべてActionのほうに書くことになります。

## Action
ADD_TODOの処理を見てみます。

```
todosRef.push(text);
```

todosRefはFirebaseのdb参照でそこに対してpushを行っています。
todosRefが変更されると、(INIT_TODOでbindingを行っているため)VuexFireが自動的に変更の内容がmutations.todosに連携されます。
つまり、Firebaseにデータを連携させるのは普通にfirebaseの機能を使って行い、Firebase側から現在の状態をstateにbindさせるときにVuexFireの機能を使っています。

## VuexFireを入れたときVuexはどうなるか？
VuexFireを使ったときのデータの流れの図です。

まず、Vuexだけの時はこんな感じ。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/115996/3eb53127-48b6-6cc9-30b6-2239e6fdd2a5.png)

Getter経由でComponentにアクセスすることやAPIとの接続が省略されているところを公式サイトとちょっと変えています。

VuexFireが入るとこんな感じ。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/115996/d347c434-18e3-c122-31c9-db83b72e53cb.png)


いままで、Actionから呼び出してたMutationがVuexFireのほうから呼び出されて、stateの状態が変更される感じです。
あくまでFirebaseと接続されるデータに着目した図なので、Firebaseと接続されないstateがあれば今まで通りのデータフローになります。

## VuexFireの役割
上でも少し述べましたが、VuexFireの役割はFirebase側からstateへのバインディングなので、双方向のやり取りをやってくれるわけではないようです。
あくまでFirebaseへの変更はfirebaseの機能を使うということですね。
最初、VuexFireというものがあると聞いて想像していたのは、設定を書けばstateの状態を監視して自動でFirebaseと連携してくれるみたいなものを想像していたのですが、そういう感じではないようです。
ふつうのVueFireだとどうなんだろう。
なんかVuexとの連携の保証がされないみたいな話がどこかにあって、そっちは使ってないけど。

# 参考
[Vue.js（v2.x.）を使ってメモアプリを作ってみた](https://qiita.com/akifo/items/7f01324fc17967a71762)
[vuexfire で Firebase と Vuex をサクッと試してみる](https://qiita.com/numanomanu/items/a0d4b148eef652ba9610)
[Vue.jsとVuexでTodoListを作ってみた](https://qiita.com/tiwu_official/items/d66c5f3fe92794536d32)