なるはや Vue SPA入門：Vuex モジュール

Vue CLI3 で作成した SPA（Single Page Application）プロジェクト上で、段階的に Vue.js を学んで行きましょう。

[目次はこちら](https://qiita.com/tsubo/items/24df49ef2f1ba87984d1)

今回は Vuex モジュール編です。

## 前提事項

[API コール編](https://qiita.com/tsubo/items/d741b52e5fea4aba2147) が完了していること。 

## Vuex モジュール

アプリケーションの全ての状態は、一つの大きなストアオブジェクトに内包されます。しかしながら、アプリケーションが大きくなるにつれて、ストアオブジェクトは膨れ上がってきます。そのため Vuex ではストアをモジュールに分割できるようになっています。

```js
const moduleA = {
  state: { ... },
  mutations: { ... },
  actions: { ... },
  getters: { ... }
}

const moduleB = {
  state: { ... },
  mutations: { ... },
  actions: { ... }
}

const store = new Vuex.Store({
  modules: {
    a: moduleA,
    b: moduleB
  }
})

store.state.a // -> `moduleA` のステート
store.state.b // -> `moduleB` のステート
```

## counter モジュール

[Vuex 編](https://qiita.com/tsubo/items/af3886cdbeb6781e7d52)で作成したカウンターを Vuex のモジュールを使うよう修正します。

```js:src/store.js
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

const counter = {
  namespaced: true, // モジュールの名前空間を有効にする

  state: {
    count: 0,
  },
  getters: {
    double(state) {
      return state.count * 2
    },
  },
  mutations: {
    increment(state) {
      state.count++
    },
    decrement(state) {
      state.count--
    },
  },
  actions: {
    increment(context) {
      context.commit('increment')
    },
    // 引数に ES2015 の引数分割束縛を使った例（context を省略できる）
    decrement({ commit }) {
      commit('decrement')
    },
  },
}

export default new Vuex.Store({
  modules: {
    counter: counter
  },
})
```
Counter.vue も修正します。

```js:src/views/Counter.vue
export default {
  // ...
  computed: {
    // counter モジュールの state を参照する
    ...mapState({
      count: state => state.counter.count
    }),
    // counter モジュールの getter を名前空間を使用して参照する
    ...mapGetters({
      double: "counter/double"
    })
  },
  methods: {
    // counter モジュールの action を名前空間を使用して参照する
    ...mapActions({
      increment: "counter/increment",
      decrement: "counter/decrement"
    })
  }
};
```

## todoList モジュール

[コンポーネント編](https://qiita.com/tsubo/items/745324a9f4719cb6b8fa)で作成した TODO List を Vuex のモジュール機能を使うよう修正します。

```js:src/store.js
// ...

const counter = {
  // ...
}

const todoList = {
  namespaced: true, // モジュールの名前空間を有効にする

  state: {
    todos: [],
  },
  getters: {
    todos(state) {
      return state.todos
    }
  },
  mutations: {
    todos(state, todos) {
      state.todos = todos
    },
    remove(state, idToRemove) {
      state.todos = state.todos.filter(todo => {
        return todo.id !== idToRemove
      })
    }
  },
  actions: {
    getTodos({ commit }) {
      fetch('http://localhost:8080/todos.json')
        .then(response => response.json())
        .then(json => {
          console.log(json)
          commit('todos', json.todos)
        })
        .catch(error => console.error('Error:', error))
    }
  }
}

export default new Vuex.Store({
  modules: {
    counter: counter,
    todoList: todoList,
  },
})
```
TodoList.vue も修正します。

```js:src/views/TodoList.vue
import TodoListItem from "../components/TodoListItem";
import TodoInputText from "../components/TodoInputText";
import { mapGetters } from "vuex";
import { mapMutations } from "vuex";
import { mapActions } from "vuex";

export default {
  components: {
    TodoListItem,
    TodoInputText
  },
  created() {
    this.getTodos();
  },
  computed: {
    ...mapGetters({
      todos: "todoList/todos"
    })
  },
  methods: {
    ...mapMutations({
      remove: "todoList/remove"
    }),
    ...mapActions({
      getTodos: "todoList/getTodos",
      add: "todoList/add"
    }),
    removeTodo(idToRemove) {
      this.remove(idToRemove);
    },
    addTodo(text) {
      this.add(text);
    }
  }
};
```

[コンポーネント編](https://qiita.com/tsubo/items/745324a9f4719cb6b8fa)の演習で TODO を追加できるようにした方は、それらの機能も Vuex のモジュールを使うよう修正してみてください。

## 動作確認

カウンター、TODO リストともに以前と同様に動くか確認してください。

## 次回

[Vuex ファイル分割](https://qiita.com/tsubo/items/771d163a3c6b807a6977)